2012-11-17 16:29:06 +01:00
# -*- coding: utf-8 -*-
2013-07-06 09:42:26 +02:00
""" License
Copyright ( C ) 2013 YunoHost
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program 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 Affero General Public License for more details .
You should have received a copy of the GNU Affero General Public License
along with this program ; if not , see http : / / www . gnu . org / licenses
"""
""" yunohost_app.py
2013-07-06 10:17:16 +02:00
Manage apps
2013-07-06 09:42:26 +02:00
"""
2012-11-17 16:29:06 +01:00
import os
import sys
import json
2013-02-25 19:54:23 +01:00
import shutil
2013-02-26 14:52:13 +01:00
import stat
2013-02-26 20:36:37 +01:00
import yaml
2013-02-27 17:55:29 +01:00
import time
2013-10-25 12:26:03 +02:00
import re
2013-10-27 14:23:52 +01:00
import socket
2013-10-26 17:29:15 +02:00
from yunohost import YunoHostError , YunoHostLDAP , win_msg , random_password , is_true , validate
2013-10-28 10:39:15 +01:00
from yunohost_domain import domain_list , domain_add
from yunohost_user import user_info , user_list
2013-10-18 15:48:01 +02:00
from yunohost_hook import hook_exec
2012-11-17 16:29:06 +01:00
2013-02-26 18:31:24 +01:00
repo_path = ' /var/cache/yunohost/repo '
apps_path = ' /usr/share/yunohost/apps '
2013-02-26 23:13:49 +01:00
apps_setting_path = ' /etc/yunohost/apps/ '
2013-02-26 18:31:24 +01:00
install_tmp = ' /tmp/yunohost/install '
app_tmp_folder = install_tmp + ' /from_file '
def app_listlists ( ) :
"""
List fetched lists
"""
list_list = [ ]
try :
for filename in os . listdir ( repo_path ) :
if ' .json ' in filename :
list_list . append ( filename [ : len ( filename ) - 5 ] )
except OSError :
raise YunoHostError ( 1 , _ ( " No list found " ) )
return { ' Lists ' : list_list }
2013-02-25 22:23:32 +01:00
2013-02-24 17:36:58 +01:00
def app_fetchlist ( url = None , name = None ) :
2012-11-17 16:29:06 +01:00
"""
2013-07-06 10:17:16 +02:00
Fetch application list from app server
2012-11-17 16:29:06 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
2013-10-07 22:49:58 +02:00
url - - URL of remote JSON list ( default http : / / app . yunohost . org / list . json )
name - - Name of the list ( default yunohost )
2012-11-17 16:29:06 +01:00
"""
# Create app path if not exists
2013-02-24 17:36:58 +01:00
try : os . listdir ( repo_path )
except OSError : os . makedirs ( repo_path )
2012-11-17 16:29:06 +01:00
2013-02-24 17:36:58 +01:00
if not url :
2013-05-29 16:11:01 +02:00
url = ' http://app.yunohost.org/list.json '
name = ' yunohost '
2013-02-24 17:36:58 +01:00
else :
if not name : raise YunoHostError ( 22 , _ ( " You must indicate a name for your custom list " ) )
2012-11-17 16:29:06 +01:00
2013-02-25 22:23:32 +01:00
if os . system ( ' wget " ' + url + ' " -O " ' + repo_path + ' / ' + name + ' .json " ' ) != 0 :
2013-02-24 17:36:58 +01:00
raise YunoHostError ( 1 , _ ( " List server connection failed " ) )
2013-02-12 13:52:11 +01:00
2013-02-24 17:36:58 +01:00
win_msg ( _ ( " List successfully fetched " ) )
2012-11-17 16:29:06 +01:00
2013-02-25 22:54:28 +01:00
def app_removelist ( name ) :
"""
2013-07-06 10:17:16 +02:00
Remove list from the repositories
2013-02-25 22:54:28 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
2013-02-25 22:54:28 +01:00
name - - Name of the list to remove
"""
try :
os . remove ( repo_path + ' / ' + name + ' .json ' )
except OSError :
raise YunoHostError ( 22 , _ ( " Unknown list " ) )
win_msg ( _ ( " List successfully removed " ) )
2013-02-25 14:04:41 +01:00
def app_list ( offset = None , limit = None , filter = None , raw = False ) :
2013-02-10 22:34:14 +01:00
"""
2013-07-06 10:17:16 +02:00
List apps
2013-02-10 22:34:14 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
limit - - Maximum number of app fetched
offset - - Starting number for app fetching
filter - - Name filter of app_id or app_name
2013-07-06 12:59:06 +02:00
raw - - Return the full app_dict
2013-02-10 22:34:14 +01:00
"""
# TODO: List installed applications
if offset : offset = int ( offset )
else : offset = 0
if limit : limit = int ( limit )
else : limit = 1000
2013-02-10 21:04:15 +01:00
2013-02-24 17:36:58 +01:00
applists = os . listdir ( repo_path )
app_dict = { }
2013-09-24 22:04:13 +02:00
if raw :
list_dict = { }
else :
list_dict = [ ]
2013-02-10 21:04:15 +01:00
2013-10-27 19:37:19 +01:00
if not applists :
app_fetchlist ( )
applists = os . listdir ( repo_path )
2013-02-24 17:36:58 +01:00
for applist in applists :
if ' .json ' in applist :
2013-02-25 22:23:32 +01:00
with open ( repo_path + ' / ' + applist ) as json_list :
2013-02-24 17:36:58 +01:00
app_dict . update ( json . loads ( str ( json_list . read ( ) ) ) )
2013-10-27 19:37:19 +01:00
for app in os . listdir ( apps_setting_path ) :
if app not in app_dict :
# Look for forks
if ' __ ' in app :
original_app = app [ : app . index ( ' __ ' ) ]
if original_app in app_dict :
app_dict [ app ] = app_dict [ original_app ]
continue
app_dict [ app ] = { ' orphan ' : True }
2013-02-10 22:34:14 +01:00
if len ( app_dict ) > ( 0 + offset ) and limit > 0 :
2013-02-24 17:36:58 +01:00
sorted_app_dict = { }
2013-09-08 14:48:07 +02:00
for sorted_keys in sorted ( app_dict . keys ( ) ) [ offset : ] :
2013-09-08 14:13:30 +02:00
sorted_app_dict [ sorted_keys ] = app_dict [ sorted_keys ]
i = 0
2013-02-10 22:34:14 +01:00
for app_id , app_info in sorted_app_dict . items ( ) :
2013-09-08 14:13:30 +02:00
if i < limit :
2013-09-08 14:48:07 +02:00
if ( filter and ( ( filter in app_id ) or ( filter in app_info [ ' manifest ' ] [ ' name ' ] ) ) ) or not filter :
2013-10-08 23:51:47 +02:00
installed = _is_installed ( app_id )
2013-02-27 19:20:45 +01:00
2013-09-08 14:48:07 +02:00
if raw :
2013-09-24 22:04:13 +02:00
app_info [ ' installed ' ] = installed
2013-09-08 14:48:07 +02:00
list_dict [ app_id ] = app_info
else :
2013-09-24 22:04:13 +02:00
list_dict . append ( {
' ID ' : app_id ,
' Name ' : app_info [ ' manifest ' ] [ ' name ' ] ,
' Description ' : app_info [ ' manifest ' ] [ ' description ' ] ,
2013-10-08 23:51:47 +02:00
' Installed ' : installed
2013-09-24 22:04:13 +02:00
} )
2013-09-08 14:48:07 +02:00
i + = 1
2013-09-08 14:13:30 +02:00
else :
2013-09-08 14:48:07 +02:00
break
2013-09-24 22:04:13 +02:00
if not raw :
list_dict = { ' Apps ' : list_dict }
2013-02-10 21:04:15 +01:00
return list_dict
2013-02-11 11:14:39 +01:00
2013-03-02 11:19:20 +01:00
2013-10-08 23:51:47 +02:00
def app_info ( app , raw = False ) :
2013-06-06 13:15:38 +02:00
"""
2013-07-06 10:17:16 +02:00
Get app informations
2013-06-06 13:15:38 +02:00
2013-07-06 10:17:16 +02:00
Keyword argument :
2013-07-06 12:59:06 +02:00
app - - App ID
2013-06-06 13:15:38 +02:00
raw - - Return the full app_dict
"""
try :
2013-10-27 19:37:19 +01:00
app_info = app_list ( filter = app , raw = True ) [ app ]
2013-06-06 13:15:38 +02:00
except YunoHostError :
app_info = { }
2013-10-27 19:37:19 +01:00
with open ( apps_setting_path + app + ' /settings.yml ' ) as f :
app_info [ ' settings ' ] = yaml . load ( f )
2013-06-06 13:15:38 +02:00
if raw :
return app_info
else :
2013-10-27 15:48:10 +01:00
return {
' Name ' : app_info [ ' manifest ' ] [ ' name ' ] ,
' Description ' : app_info [ ' manifest ' ] [ ' description ' ] [ ' en ' ] ,
2013-06-06 13:15:38 +02:00
#TODO: Add more infos
2013-10-27 15:48:10 +01:00
}
2013-06-06 13:15:38 +02:00
2013-10-27 19:37:19 +01:00
def app_map ( app = None , raw = False , user = None ) :
2013-03-02 11:19:20 +01:00
"""
2013-07-06 10:17:16 +02:00
List apps by domain
2013-03-02 11:19:20 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
app - - Specific app to map
2013-06-07 13:44:23 +02:00
raw - - Return complete dict
2013-10-27 19:37:19 +01:00
user - - Only accessible app for user
2013-06-07 13:44:23 +02:00
2013-03-02 11:19:20 +01:00
"""
result = { }
2013-10-08 23:51:47 +02:00
for app_id in os . listdir ( apps_setting_path ) :
if app and ( app != app_id ) :
2013-06-07 13:44:23 +02:00
continue
2013-03-02 11:20:55 +01:00
2013-10-27 19:37:19 +01:00
if user is not None :
app_dict = app_info ( app = app_id , raw = True )
if ( ' mode ' not in app_dict [ ' settings ' ] ) or ( ' mode ' in app_dict [ ' settings ' ] and app_dict [ ' settings ' ] [ ' mode ' ] == ' private ' ) :
if ' allowed_users ' in app_dict [ ' settings ' ] and user not in app_dict [ ' settings ' ] [ ' allowed_users ' ] . split ( ' , ' ) :
continue
2013-10-08 23:51:47 +02:00
with open ( apps_setting_path + app_id + ' /settings.yml ' ) as f :
2013-06-07 13:44:23 +02:00
app_settings = yaml . load ( f )
2013-10-08 23:51:47 +02:00
if ' domain ' not in app_settings :
continue
2013-06-07 13:44:23 +02:00
if raw :
if app_settings [ ' domain ' ] not in result :
result [ app_settings [ ' domain ' ] ] = { }
result [ app_settings [ ' domain ' ] ] [ app_settings [ ' path ' ] ] = {
' label ' : app_settings [ ' label ' ] ,
2013-10-26 17:29:15 +02:00
' id ' : app_settings [ ' id ' ]
2013-06-07 13:44:23 +02:00
}
else :
2013-10-26 17:29:15 +02:00
result [ app_settings [ ' domain ' ] + app_settings [ ' path ' ] ] = app_settings [ ' label ' ]
2013-03-02 11:19:20 +01:00
return result
2013-06-07 13:44:23 +02:00
2013-10-08 23:51:47 +02:00
def app_upgrade ( app , url = None , file = None ) :
2013-06-06 13:15:38 +02:00
"""
2013-07-06 10:17:16 +02:00
Upgrade app
2013-06-06 13:15:38 +02:00
2013-07-06 10:17:16 +02:00
Keyword argument :
url - - Git url to fetch for upgrade
app - - App ( s ) to upgrade ( default all )
2013-06-06 13:15:38 +02:00
file - - Folder or tarball for upgrade
"""
with YunoHostLDAP ( ) as yldap :
try :
app_list ( )
except YunoHostError :
raise YunoHostError ( 1 , _ ( " No app to upgrade " ) )
upgraded_apps = [ ]
2013-10-09 00:04:52 +02:00
# If no app is specified, upgrade all apps
2013-06-06 13:15:38 +02:00
if not app :
2013-10-09 00:04:52 +02:00
app = os . listdir ( apps_setting_path )
2013-06-06 13:15:38 +02:00
for app_id in app :
2013-10-09 00:04:52 +02:00
installed = _is_installed ( app_id )
if not installed :
2013-06-06 13:15:38 +02:00
raise YunoHostError ( 1 , app_id + _ ( " is not installed " ) )
2013-10-09 00:04:52 +02:00
if app_id in upgraded_apps :
2013-10-18 15:48:01 +02:00
continue
2013-06-06 13:15:38 +02:00
2013-10-09 00:04:52 +02:00
#TODO: fix that (and check for instance number)
current_app_dict = app_info ( app_id , instance = number , raw = True )
new_app_dict = app_info ( app_id , raw = True )
2013-06-06 13:15:38 +02:00
2013-10-09 00:04:52 +02:00
if file :
manifest = _extract_app_from_file ( file )
elif url :
manifest = _fetch_app_from_git ( url )
elif ( new_app_dict [ ' lastUpdate ' ] > current_app_dict [ ' lastUpdate ' ] ) or ( ' update_time ' not in current_app_dict [ ' settings ' ] and ( new_app_dict [ ' lastUpdate ' ] > current_app_dict [ ' settings ' ] [ ' install_time ' ] ) ) or ( ' update_time ' in current_app_dict [ ' settings ' ] and ( new_app_dict [ ' lastUpdate ' ] > current_app_dict [ ' settings ' ] [ ' update_time ' ] ) ) :
manifest = _fetch_app_from_git ( app_id )
else :
continue
2013-06-06 13:15:38 +02:00
2013-10-09 00:04:52 +02:00
app_setting_path = apps_setting_path + ' / ' + app_id
2013-06-06 13:15:38 +02:00
2013-10-18 15:48:01 +02:00
# Execute App upgrade script
if hook_exec ( app_setting_path + ' /scripts/upgrade ' ) != 0 :
#TODO: display fail messages from script
pass
else :
2013-10-18 16:55:03 +02:00
app_setting ( app_id , ' update_time ' , int ( time . time ( ) ) )
2013-06-06 13:15:38 +02:00
2013-10-18 15:48:01 +02:00
# Move scripts and manifest to the right place
os . system ( ' mv " ' + app_tmp_folder + ' /manifest.json " " ' + app_tmp_folder + ' /scripts " ' + app_setting_path )
2013-06-06 13:15:38 +02:00
2013-10-09 11:05:04 +02:00
# So much win
upgraded_apps . append ( app_id )
2013-10-09 00:04:52 +02:00
win_msg ( app_id + _ ( " upgraded successfully " ) )
2013-06-06 13:15:38 +02:00
if not upgraded_apps :
raise YunoHostError ( 1 , _ ( " No app to upgrade " ) )
win_msg ( _ ( " Upgrade complete " ) )
2013-03-02 11:19:20 +01:00
2013-10-07 22:49:58 +02:00
def app_install ( app , label = None ) :
2013-02-11 11:14:39 +01:00
"""
2013-07-06 10:17:16 +02:00
Install apps
2013-02-11 11:14:39 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
2013-10-07 22:49:58 +02:00
app - - App ID to install
2013-07-06 10:17:16 +02:00
label
2013-02-11 11:14:39 +01:00
"""
2013-10-17 11:38:55 +02:00
#TODO: Create tool for nginx (check path availability & stuff)
2013-02-11 11:14:39 +01:00
2013-02-25 19:54:23 +01:00
with YunoHostLDAP ( ) as yldap :
2013-03-01 20:03:12 +01:00
2013-10-09 11:05:04 +02:00
# Fetch or extract sources
2013-02-25 19:54:23 +01:00
try : os . listdir ( install_tmp )
except OSError : os . makedirs ( install_tmp )
2013-02-11 13:45:58 +01:00
2013-06-06 13:15:38 +02:00
if app in app_list ( raw = True ) or ( ' @ ' in app ) or ( ' http:// ' in app ) or ( ' https:// ' in app ) :
2013-02-26 14:52:13 +01:00
manifest = _fetch_app_from_git ( app )
2013-03-02 12:09:22 +01:00
else :
manifest = _extract_app_from_file ( app )
2013-02-11 13:45:58 +01:00
2013-10-09 11:05:04 +02:00
# Check ID
if ' id ' not in manifest or ' __ ' in manifest [ ' id ' ] :
2013-10-08 23:51:47 +02:00
raise YunoHostError ( 22 , _ ( " App id is invalid " ) )
2013-02-11 11:14:39 +01:00
2013-10-09 11:05:04 +02:00
app_id = manifest [ ' id ' ]
2013-03-01 20:03:12 +01:00
2013-10-09 11:05:04 +02:00
# Check if app can be forked
instance_number = _installed_instance_number ( app_id , last = True ) + 1
2013-10-18 16:55:03 +02:00
if instance_number > 1 :
2013-10-09 11:05:04 +02:00
if ' multi_instance ' not in manifest or not is_true ( manifest [ ' multi_instance ' ] ) :
raise YunoHostError ( 1 , _ ( " App is already installed " ) )
2013-02-11 11:14:39 +01:00
2013-10-25 12:26:03 +02:00
app_id_forked = app_id + ' __ ' + str ( instance_number )
2013-02-11 11:14:39 +01:00
2013-10-09 11:05:04 +02:00
# Replace app_id with the new one in scripts
for file in os . listdir ( app_tmp_folder + ' /scripts ' ) :
#TODO: add hooks directory to the list
#TODO: do it with sed ?
2013-10-25 12:26:03 +02:00
if file [ : 1 ] != ' . ' :
with open ( app_tmp_folder + ' /scripts/ ' + file , " r " ) as sources :
lines = sources . readlines ( )
with open ( app_tmp_folder + ' /scripts/ ' + file , " w " ) as sources :
for line in lines :
sources . write ( re . sub ( r ' ' + app_id + ' ' , app_id_forked , line ) )
2013-02-26 23:13:49 +01:00
2013-10-09 11:05:04 +02:00
# Change app_id for the rest of the process
app_id = app_id_forked
2013-03-01 20:03:12 +01:00
2013-10-09 11:05:04 +02:00
# Prepare App settings
app_setting_path = apps_setting_path + ' / ' + app_id
2013-03-01 20:03:12 +01:00
2013-10-09 11:05:04 +02:00
#TMP: Remove old settings
2013-02-26 23:13:49 +01:00
if os . path . exists ( app_setting_path ) : shutil . rmtree ( app_setting_path )
os . makedirs ( app_setting_path )
2013-10-18 15:48:01 +02:00
os . system ( ' touch ' + app_setting_path + ' /settings.yml ' )
2013-10-18 16:55:03 +02:00
2013-10-18 15:48:01 +02:00
app_setting ( app_id , ' id ' , app_id )
app_setting ( app_id , ' install_time ' , int ( time . time ( ) ) )
2013-02-11 11:14:39 +01:00
2013-10-18 15:48:01 +02:00
if label :
app_setting ( app_id , ' label ' , label )
else :
app_setting ( app_id , ' label ' , manifest [ ' name ' ] )
2013-02-26 20:36:37 +01:00
2013-10-25 12:11:24 +02:00
os . system ( ' chown -R admin: ' + app_tmp_folder )
2013-10-09 11:05:04 +02:00
# Execute App install script
2013-10-25 12:11:24 +02:00
if hook_exec ( app_tmp_folder + ' /scripts/install ' ) == 0 :
# Move scripts and manifest to the right place
os . system ( ' mv " ' + app_tmp_folder + ' /manifest.json " " ' + app_tmp_folder + ' /scripts " ' + app_setting_path )
shutil . rmtree ( app_tmp_folder )
os . system ( ' chmod -R 400 ' + app_setting_path )
os . system ( ' chown -R root: ' + app_setting_path )
2013-10-26 17:29:15 +02:00
os . system ( ' chown -R admin: ' + app_setting_path + ' /scripts ' )
2013-10-25 12:11:24 +02:00
win_msg ( _ ( " Installation complete " ) )
else :
2013-10-09 11:05:04 +02:00
#TODO: display script fail messages
shutil . rmtree ( app_setting_path )
2013-10-25 12:11:24 +02:00
raise YunoHostError ( 1 , _ ( " Installation failed " ) )
2013-03-01 20:03:12 +01:00
2013-10-09 11:05:04 +02:00
def app_remove ( app ) :
2013-06-07 13:44:23 +02:00
"""
2013-07-06 10:17:16 +02:00
Remove app
2013-06-07 13:44:23 +02:00
2013-07-06 10:17:16 +02:00
Keyword argument :
app - - App ( s ) to delete
2013-06-07 13:44:23 +02:00
"""
2013-10-09 11:05:04 +02:00
if not _is_installed ( app ) :
raise YunoHostError ( 22 , _ ( " App is not installed " ) )
2013-06-07 13:44:23 +02:00
2013-10-25 12:11:24 +02:00
app_setting_path = apps_setting_path + app
2013-10-09 11:05:04 +02:00
#TODO: display fail messages from script
2013-10-25 12:11:24 +02:00
if hook_exec ( app_setting_path + ' /scripts/remove ' ) != 0 :
2013-10-18 15:48:01 +02:00
pass
2013-06-07 13:44:23 +02:00
2013-10-18 16:55:03 +02:00
if os . path . exists ( app_setting_path ) : shutil . rmtree ( app_setting_path )
2013-10-09 11:05:04 +02:00
win_msg ( _ ( " App removed: " ) + app )
2013-06-07 13:44:23 +02:00
2013-03-03 15:54:24 +01:00
def app_addaccess ( apps , users ) :
"""
2013-07-06 10:17:16 +02:00
Grant access right to users ( everyone by default )
2013-03-03 15:54:24 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
users
2013-07-06 12:59:06 +02:00
apps
2013-03-03 15:54:24 +01:00
"""
2013-10-17 11:38:55 +02:00
#TODO: Adapt to SSOwat
2013-03-03 15:54:24 +01:00
if not isinstance ( users , list ) : users = [ users ]
if not isinstance ( apps , list ) : apps = [ apps ]
2013-10-09 11:05:04 +02:00
for app in apps :
if not _is_installed ( app ) :
raise YunoHostError ( 22 , _ ( " App is not installed " ) )
2013-03-03 15:54:24 +01:00
2013-10-09 11:05:04 +02:00
with open ( apps_setting_path + app + ' /settings.yml ' ) as f :
app_settings = yaml . load ( f )
2013-03-03 16:07:07 +01:00
2013-10-27 15:48:10 +01:00
if ' mode ' not in app_settings :
app_setting ( app , ' mode ' , ' private ' )
app_settings [ ' mode ' ] = ' private '
if app_settings [ ' mode ' ] == ' private ' :
2013-10-09 11:05:04 +02:00
if ' allowed_users ' in app_settings :
new_users = app_settings [ ' allowed_users ' ]
else :
new_users = ' '
for allowed_user in users :
2013-10-17 11:38:55 +02:00
if allowed_user not in new_users . split ( ' , ' ) :
2013-10-09 11:05:04 +02:00
try :
user_info ( allowed_user )
except YunoHostError :
continue
2013-10-27 15:48:10 +01:00
if new_users == ' ' :
new_users = allowed_user
else :
new_users = new_users + ' , ' + allowed_user
2013-10-09 11:05:04 +02:00
2013-10-27 15:48:10 +01:00
app_setting ( app , ' allowed_users ' , new_users . strip ( ) )
2013-03-03 15:54:24 +01:00
2013-10-28 10:39:15 +01:00
app_ssowatconf ( )
2013-03-03 17:45:31 +01:00
def app_removeaccess ( apps , users ) :
"""
2013-07-06 10:17:16 +02:00
Revoke access right to users ( everyone by default )
2013-03-03 17:45:31 +01:00
2013-07-06 10:17:16 +02:00
Keyword argument :
users
2013-07-06 12:59:06 +02:00
apps
2013-03-03 17:45:31 +01:00
"""
2013-10-17 11:38:55 +02:00
#TODO: Remove access
2013-03-03 17:45:31 +01:00
if not isinstance ( users , list ) : users = [ users ]
if not isinstance ( apps , list ) : apps = [ apps ]
2013-10-09 11:05:04 +02:00
for app in apps :
new_users = ' '
2013-03-03 17:45:31 +01:00
2013-10-09 11:05:04 +02:00
if not _is_installed ( app ) :
raise YunoHostError ( 22 , _ ( " App is not installed " ) )
2013-03-03 17:45:31 +01:00
2013-10-09 11:05:04 +02:00
with open ( apps_setting_path + app + ' /settings.yml ' ) as f :
app_settings = yaml . load ( f )
2013-03-03 17:45:31 +01:00
2013-10-09 11:05:04 +02:00
if ' mode ' in app_settings and app_settings [ ' mode ' ] == ' private ' :
if ' allowed_users ' in app_settings :
2013-10-17 11:38:55 +02:00
for allowed_user in app_settings [ ' allowed_users ' ] . split ( ' , ' ) :
2013-10-09 11:05:04 +02:00
if allowed_user not in users :
2013-10-27 15:48:10 +01:00
if new_users == ' ' :
new_users = allowed_user
else :
new_users = new_users + ' , ' + allowed_user
2013-03-03 17:45:31 +01:00
2013-10-27 15:48:10 +01:00
app_setting ( app , ' allowed_users ' , new_users . strip ( ) )
2013-03-03 17:45:31 +01:00
2013-10-28 10:39:15 +01:00
app_ssowatconf ( )
2013-03-03 15:54:24 +01:00
2013-03-01 20:03:12 +01:00
2013-10-18 15:48:01 +02:00
def app_setting ( app , key , value = None ) :
"""
"""
settings_file = apps_setting_path + app + ' /settings.yml '
2013-10-26 17:29:15 +02:00
try :
with open ( settings_file ) as f :
app_settings = yaml . load ( f )
except IOError :
# Do not fail if setting file is not there
pass
2013-10-18 15:48:01 +02:00
2013-10-26 17:29:15 +02:00
if value is None :
# Get the value
if app_settings is not None and key in app_settings :
print ( app_settings [ key ] )
else :
# Set the value
if app_settings is None :
app_settings = { }
2013-10-18 15:48:01 +02:00
if value == ' ' and key in app_settings :
del app_settings [ key ]
else :
app_settings [ key ] = value
2013-10-26 17:29:15 +02:00
with open ( settings_file , ' w ' ) as f :
yaml . safe_dump ( app_settings , f , default_flow_style = False )
2013-10-18 15:48:01 +02:00
2013-10-27 14:23:52 +01:00
def app_checkport ( port ) :
"""
"""
try :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
s . settimeout ( 1 )
s . connect ( ( " localhost " , int ( port ) ) )
s . close ( )
except socket . error :
win_msg ( _ ( " Port available: " ) + str ( port ) )
else :
raise YunoHostError ( 22 , _ ( " Port not available " ) )
2013-10-26 17:29:15 +02:00
def app_checkurl ( url , app = None ) :
"""
"""
if " https:// " == url [ : 8 ] :
url = url [ 8 : ]
elif " http:// " == url [ : 7 ] :
url = url [ 7 : ]
if url [ - 1 : ] != ' / ' :
url = url + ' / '
domain = url [ : url . index ( ' / ' ) ]
path = url [ url . index ( ' / ' ) : ]
if path [ - 1 : ] != ' / ' :
path = path + ' / '
apps_map = app_map ( raw = True )
validate ( r ' ^([a-zA-Z0-9] {1} ([a-zA-Z0-9 \ -]*[a-zA-Z0-9])*)( \ .[a-zA-Z0-9] {1} ([a-zA-Z0-9 \ -]*[a-zA-Z0-9])*)*( \ .[a-zA-Z] {1} ([a-zA-Z0-9 \ -]*[a-zA-Z0-9])*)$ ' , domain )
if domain not in domain_list ( ) [ ' Domains ' ] :
raise YunoHostError ( 22 , _ ( " Domain doesn ' t exists " ) )
if domain in apps_map :
if path in apps_map [ domain ] :
raise YunoHostError ( 1 , _ ( " An app is already installed on this location " ) )
for app_path , v in apps_map [ domain ] . items ( ) :
if app_path in path and app_path . count ( ' / ' ) < path . count ( ' / ' ) :
raise YunoHostError ( 1 , _ ( " Unable to install app at this location " ) )
if app is not None :
app_setting ( app , ' domain ' , value = domain )
app_setting ( app , ' path ' , value = path )
def app_initdb ( user , password = None , db = None , sql = None ) :
"""
Create database and initialize it with optionnal attached script
Keyword arguments :
user - - Name of the DB user
password - - Password for the user
db - - Database name ( optionnal )
sql - - Initial SQL file
"""
if db is None :
db = user
return_pwd = False
if password is None :
password = random_password ( 12 )
return_pwd = True
print ( password )
mysql_root_pwd = open ( ' /etc/yunohost/mysql ' ) . read ( ) . rstrip ( )
mysql_command = ' mysql -u root -p ' + mysql_root_pwd + ' -e " CREATE DATABASE ' + db + ' ; GRANT ALL PRIVILEGES ON ' + db + ' .* TO \' ' + user + ' \' @localhost IDENTIFIED BY \' ' + password + ' \' ; " '
if os . system ( mysql_command ) != 0 :
raise YunoHostError ( 1 , _ ( " MySQL DB creation failed " ) )
if sql is not None :
if os . system ( ' mysql -u ' + user + ' -p ' + password + ' ' + db + ' < ' + sql ) != 0 :
raise YunoHostError ( 1 , _ ( " MySQL DB init failed " ) )
if not return_pwd :
win_msg ( _ ( " Database initiliazed " ) )
2013-10-25 12:11:24 +02:00
2013-10-28 10:39:15 +01:00
def app_ssowatconf ( ) :
"""
Regenerate SSOwat conf from YunoHost settings
Keyword argument :
"""
with open ( ' /etc/yunohost/current_host ' , ' r ' ) as f :
main_domain = f . readline ( ) . rstrip ( )
domains = domain_list ( ) [ ' Domains ' ]
apps = { }
for app , v in app_list ( raw = True ) . items ( ) :
app_settings = app_info ( raw = True , app = app ) [ ' settings ' ]
if ' domain ' in app_settings :
if ' path ' not in app_settings :
app_settings [ ' path ' ] = ' / '
if ' mode ' not in app_settings :
app_settings [ ' mode ' ] = ' private '
if ' allowed_users ' not in app_settings :
app_settings [ ' allowed_users ' ] = ' '
apps [ app ] = {
' domain ' : app_settings [ ' domain ' ] ,
' path ' : app_settings [ ' path ' ] ,
' mode ' : app_settings [ ' mode ' ] ,
' allowed_users ' : app_settings [ ' allowed_users ' ]
}
users = { }
for user in user_list ( ) [ ' Users ' ] :
users [ user [ ' Username ' ] ] = app_map ( user = user [ ' Username ' ] )
conf_dict = {
' portal_domain ' : main_domain ,
' portal_path ' : ' /ynhsso/ ' ,
' portal_port ' : ' 443 ' ,
' portal_scheme ' : ' https ' ,
' additional_headers ' : {
' Auth-User ' : ' uid ' ,
' Remote-User ' : ' uid ' ,
' Name ' : ' cn ' ,
' Email ' : ' mail '
} ,
' domains ' : domains ,
' skipped_urls ' : [ ' https:// ' + main_domain + ' /ynhadmin ' ] ,
' unprotected_urls ' : [ ] ,
' apps ' : apps ,
' users ' : users
}
with open ( ' /etc/ssowat/conf.json ' , ' wb ' ) as f :
json . dump ( conf_dict , f )
win_msg ( _ ( ' SSOwat configuration generated ' ) )
2013-10-25 10:28:50 +02:00
def _extract_app_from_file ( path , remove = False ) :
2013-02-25 22:23:32 +01:00
"""
2013-03-02 12:09:22 +01:00
Unzip or untar application tarball in app_tmp_folder , or copy it from a directory
2013-02-25 22:23:32 +01:00
Keyword arguments :
2013-03-02 12:09:22 +01:00
path - - Path of the tarball or directory
2013-10-25 10:28:50 +02:00
remove - - Remove the tarball after extraction
2013-02-25 22:23:32 +01:00
Returns :
Dict manifest
"""
2013-10-26 17:29:15 +02:00
global app_tmp_folder
2013-02-25 22:23:32 +01:00
if os . path . exists ( app_tmp_folder ) : shutil . rmtree ( app_tmp_folder )
os . makedirs ( app_tmp_folder )
2013-03-02 12:09:22 +01:00
2013-02-26 14:52:13 +01:00
if " .zip " in path :
2013-03-02 12:09:22 +01:00
extract_result = os . system ( ' cd ' + os . getcwd ( ) + ' && unzip ' + path + ' -d ' + app_tmp_folder )
2013-10-25 10:28:50 +02:00
if remove : os . remove ( path )
2013-02-26 14:52:13 +01:00
elif " .tar " in path :
2013-03-02 12:09:22 +01:00
extract_result = os . system ( ' cd ' + os . getcwd ( ) + ' && tar -xf ' + path + ' -C ' + app_tmp_folder )
2013-10-25 10:28:50 +02:00
if remove : os . remove ( path )
2013-03-02 12:09:22 +01:00
elif ( path [ : 1 ] == ' / ' and os . path . exists ( path ) ) or ( os . system ( ' cd ' + os . getcwd ( ) + ' / ' + path ) == 0 ) :
shutil . rmtree ( app_tmp_folder )
if path [ len ( path ) - 1 : ] != ' / ' :
path = path + ' / '
extract_result = os . system ( ' cd ' + os . getcwd ( ) + ' && cp -a " ' + path + ' " ' + app_tmp_folder )
2013-02-25 22:23:32 +01:00
else :
extract_result = 1
if extract_result != 0 :
raise YunoHostError ( 22 , _ ( " Invalid install file " ) )
2013-03-02 12:09:22 +01:00
try :
2013-10-26 17:29:15 +02:00
if len ( os . listdir ( app_tmp_folder ) ) == 1 :
for folder in os . listdir ( app_tmp_folder ) :
app_tmp_folder = app_tmp_folder + ' / ' + folder
2013-10-07 22:49:58 +02:00
with open ( app_tmp_folder + ' /manifest.json ' ) as json_manifest :
2013-03-02 12:09:22 +01:00
manifest = json . loads ( str ( json_manifest . read ( ) ) )
manifest [ ' lastUpdate ' ] = int ( time . time ( ) )
except IOError :
raise YunoHostError ( 1 , _ ( " Invalid App file " ) )
2013-02-25 22:23:32 +01:00
2013-03-02 12:09:22 +01:00
win_msg ( _ ( " Sources extracted " ) )
2013-02-26 14:52:13 +01:00
2013-02-25 22:23:32 +01:00
return manifest
2013-02-26 14:52:13 +01:00
def _fetch_app_from_git ( app ) :
2013-02-25 22:23:32 +01:00
"""
Unzip or untar application tarball in app_tmp_folder
2013-02-11 11:14:39 +01:00
2013-02-25 22:23:32 +01:00
Keyword arguments :
2013-06-06 13:15:38 +02:00
app - - App_id or git repo URL
2013-02-25 22:23:32 +01:00
Returns :
Dict manifest
"""
global app_tmp_folder
2013-06-06 13:15:38 +02:00
if ( ' @ ' in app ) or ( ' http:// ' in app ) or ( ' https:// ' in app ) :
2013-10-26 17:29:15 +02:00
if " github.com " in app :
url = app . replace ( " git@github.com: " , " https://github.com/ " )
if " .git " in url [ - 4 : ] : url = url [ : - 4 ]
if " / " in url [ - 1 : ] : url = url [ : - 1 ]
url = url + " /archive/master.zip "
if os . system ( ' wget " ' + url + ' " -O " ' + app_tmp_folder + ' .zip " ' ) == 0 :
return _extract_app_from_file ( app_tmp_folder + ' .zip ' , remove = True )
2013-06-06 13:15:38 +02:00
git_result = os . system ( ' git clone ' + app + ' ' + app_tmp_folder )
git_result_2 = 0
try :
2013-10-07 22:49:58 +02:00
with open ( app_tmp_folder + ' /manifest.json ' ) as json_manifest :
2013-06-06 13:15:38 +02:00
manifest = json . loads ( str ( json_manifest . read ( ) ) )
manifest [ ' lastUpdate ' ] = int ( time . time ( ) )
except IOError :
raise YunoHostError ( 1 , _ ( " Invalid App manifest " ) )
2013-02-25 22:23:32 +01:00
else :
2013-06-06 13:15:38 +02:00
app_dict = app_list ( raw = True )
if app in app_dict :
app_info = app_dict [ app ]
app_info [ ' manifest ' ] [ ' lastUpdate ' ] = app_info [ ' lastUpdate ' ]
manifest = app_info [ ' manifest ' ]
else :
raise YunoHostError ( 22 , _ ( " App doesn ' t exists " ) )
2013-10-25 10:28:50 +02:00
if " github.com " in app_info [ ' git ' ] [ ' url ' ] :
url = app_info [ ' git ' ] [ ' url ' ] . replace ( " git@github.com: " , " https://github.com/ " )
if " .git " in url [ - 4 : ] : url = url [ : - 4 ]
if " / " in url [ - 1 : ] : url = url [ : - 1 ]
url = url + " /archive/ " + str ( app_info [ ' git ' ] [ ' revision ' ] ) + " .zip "
if os . system ( ' wget " ' + url + ' " -O " ' + app_tmp_folder + ' .zip " ' ) == 0 :
return _extract_app_from_file ( app_tmp_folder + ' .zip ' , remove = True )
app_tmp_folder = install_tmp + ' / ' + app
if os . path . exists ( app_tmp_folder ) : shutil . rmtree ( app_tmp_folder )
2013-06-06 13:15:38 +02:00
git_result = os . system ( ' git clone ' + app_info [ ' git ' ] [ ' url ' ] + ' -b ' + app_info [ ' git ' ] [ ' branch ' ] + ' ' + app_tmp_folder )
git_result_2 = os . system ( ' cd ' + app_tmp_folder + ' && git reset --hard ' + str ( app_info [ ' git ' ] [ ' revision ' ] ) )
2013-02-25 22:23:32 +01:00
if not git_result == git_result_2 == 0 :
raise YunoHostError ( 22 , _ ( " Sources fetching failed " ) )
2013-02-26 14:52:13 +01:00
win_msg ( _ ( " Repository fetched " ) )
2013-06-06 13:15:38 +02:00
return manifest
2013-02-25 22:23:32 +01:00
2013-06-07 13:44:23 +02:00
def _installed_instance_number ( app , last = False ) :
2013-02-26 23:13:49 +01:00
"""
Check if application is installed and return instance number
Keyword arguments :
2013-10-08 23:51:47 +02:00
app - - id of App to check
2013-06-07 13:44:23 +02:00
last - - Return only last instance number
2013-02-26 23:13:49 +01:00
Returns :
2013-06-07 13:44:23 +02:00
Number of last installed instance | List or instances
2013-02-26 23:13:49 +01:00
"""
2013-06-07 13:44:23 +02:00
if last :
number = 0
try :
installed_apps = os . listdir ( apps_setting_path )
except OSError :
os . makedirs ( apps_setting_path )
return 0
2013-02-28 17:58:18 +01:00
2013-06-07 13:44:23 +02:00
for installed_app in installed_apps :
2013-10-25 12:35:23 +02:00
if number == 0 and app == installed_app :
number = 1
elif ' __ ' in installed_app :
2013-06-07 13:44:23 +02:00
if app == installed_app [ : installed_app . index ( ' __ ' ) ] :
if int ( installed_app [ installed_app . index ( ' __ ' ) + 2 : ] ) > number :
number = int ( installed_app [ installed_app . index ( ' __ ' ) + 2 : ] )
return number
else :
instance_number_list = [ ]
instances_dict = app_map ( app = app , raw = True )
for key , domain in instances_dict . items ( ) :
for key , path in domain . items ( ) :
instance_number_list . append ( path [ ' instance ' ] )
return sorted ( instance_number_list )
2013-02-26 23:13:49 +01:00
2013-10-09 11:05:04 +02:00
def _is_installed ( app ) :
"""
Check if application is installed
Keyword arguments :
app - - id of App to check
Returns :
Boolean
"""
try :
installed_apps = os . listdir ( apps_setting_path )
except OSError :
os . makedirs ( apps_setting_path )
return False
for installed_app in installed_apps :
if app == installed_app :
return True
else :
continue
return False