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-03-02 15:15:57 +01:00
from yunohost import YunoHostError , YunoHostLDAP , win_msg , random_password , lvl , is_true , lemon_configuration
2013-02-12 13:52:11 +01:00
from yunohost_domain import domain_list , domain_add
2013-03-03 18:56:27 +01:00
from yunohost_user import user_info
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-06-22 13:24:47 +02:00
a2_settings_path = ' /etc/yunohost/apache/domains '
2013-03-02 19:00:44 +01:00
a2_template_path = ' /etc/yunohost/apache/templates '
2013-02-26 18:31:24 +01:00
install_tmp = ' /tmp/yunohost/install '
app_tmp_folder = install_tmp + ' /from_file '
2013-03-01 18:24:37 +01:00
lemon_tmp_conf = ' /tmp/tmplemonconf '
2013-02-26 18:31:24 +01:00
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 :
url - - URL of remote JSON list ( default http : / / fapp . yunohost . org / app / list / raw )
name - - Name of the list ( default fapp )
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-02-10 21:04:15 +01:00
list_dict = { }
2013-02-24 17:36:58 +01:00
if not applists : app_fetchlist ( )
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-02-10 22:34:14 +01:00
if len ( app_dict ) > ( 0 + offset ) and limit > 0 :
i = 0 + offset
2013-02-24 17:36:58 +01:00
sorted_app_dict = { }
2013-02-10 22:34:14 +01:00
for sorted_keys in sorted ( app_dict . keys ( ) ) [ i : ] :
if i < = limit :
sorted_app_dict [ sorted_keys ] = app_dict [ sorted_keys ]
i + = 1
for app_id , app_info in sorted_app_dict . items ( ) :
2013-02-25 14:04:41 +01:00
if ( filter and ( ( filter in app_id ) or ( filter in app_info [ ' manifest ' ] [ ' name ' ] ) ) ) or not filter :
2013-06-07 13:44:23 +02:00
instance_number = len ( _installed_instance_number ( app_id ) )
2013-02-27 19:20:45 +01:00
if instance_number > 1 :
installed_txt = ' Yes ( ' + str ( instance_number ) + ' times) '
elif instance_number == 1 :
installed_txt = ' Yes '
else :
installed_txt = ' No '
2013-02-25 14:04:41 +01:00
if raw :
list_dict [ app_id ] = app_info
else :
2013-02-27 20:06:17 +01:00
list_dict [ app_id ] = [
( ' Name ' , app_info [ ' manifest ' ] [ ' name ' ] ) ,
( ' Version ' , app_info [ ' manifest ' ] [ ' version ' ] ) ,
( ' Description ' , app_info [ ' manifest ' ] [ ' description ' ] ) ,
( ' Installed ' , installed_txt )
]
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-06-06 13:15:38 +02:00
def app_info ( app , instance = None , raw = False ) :
"""
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-07-06 10:17:16 +02:00
instance - - App instance number
2013-06-06 13:15:38 +02:00
raw - - Return the full app_dict
"""
try :
app_info = app_list ( filter = app , limit = 1 , raw = True ) [ app ]
except YunoHostError :
app_info = { }
# If installed
2013-06-07 13:44:23 +02:00
instance_number = len ( _installed_instance_number ( app ) )
2013-06-06 13:15:38 +02:00
if instance_number > 0 and instance :
unique_app_id = app + ' __ ' + instance
with open ( apps_setting_path + unique_app_id + ' /manifest.webapp ' ) as json_manifest :
app_info [ ' manifest ' ] = json . loads ( str ( json_manifest . read ( ) ) )
with open ( apps_setting_path + unique_app_id + ' /app_settings.yml ' ) as f :
app_info [ ' settings ' ] = yaml . load ( f )
if raw :
return app_info
else :
return [
( ' Name ' , app_info [ ' manifest ' ] [ ' name ' ] ) ,
( ' Version ' , app_info [ ' manifest ' ] [ ' version ' ] ) ,
( ' Description ' , app_info [ ' manifest ' ] [ ' description ' ] ) ,
#TODO: Add more infos
]
2013-06-07 13:44:23 +02:00
def app_map ( app = None , raw = False ) :
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-03-02 11:19:20 +01:00
"""
result = { }
2013-06-07 13:44:23 +02:00
for unique_app_id in os . listdir ( apps_setting_path ) :
if app and ( app != unique_app_id ) and ( app != unique_app_id [ : unique_app_id . find ( ' __ ' ) ] ) :
continue
2013-03-02 11:20:55 +01:00
2013-06-07 13:44:23 +02:00
with open ( apps_setting_path + unique_app_id + ' /app_settings.yml ' ) as f :
app_settings = yaml . load ( f )
if raw :
if app_settings [ ' domain ' ] not in result :
result [ app_settings [ ' domain ' ] ] = { }
result [ app_settings [ ' domain ' ] ] [ app_settings [ ' path ' ] ] = {
' label ' : app_settings [ ' label ' ] ,
' uid ' : app_settings [ ' uid ' ] ,
' instance ' : app_settings [ ' instance ' ] ,
}
else :
result [ ' https:// ' + 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-06-06 13:15:38 +02:00
def app_upgrade ( app , instance = [ ] , url = None , file = None ) :
"""
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
2013-07-06 12:59:06 +02:00
instance - - App instance number to upgrade
2013-06-06 13:15:38 +02:00
"""
with YunoHostLDAP ( ) as yldap :
try :
app_list ( )
except YunoHostError :
raise YunoHostError ( 1 , _ ( " No app to upgrade " ) )
upgraded_apps = [ ]
if not app :
unique_app_id_list = os . listdir ( apps_setting_path )
for unique_app_id in unique_app_id_list :
app_id = unique_app_id [ : unique_app_id . find ( ' __ ' ) ]
if app_id not in app :
app . append ( app_id )
for app_id in app :
instance_number = _installed_instance_number ( app_id )
if instance_number == 0 :
raise YunoHostError ( 1 , app_id + _ ( " is not installed " ) )
elif not instance :
instance = range ( 1 , instance_number + 1 )
for number in instance :
if int ( number ) > instance_number :
continue
number = str ( number )
unique_app_id = app_id + ' __ ' + number
if unique_app_id in upgraded_apps :
raise YunoHostError ( 1 , _ ( " Conflict, multiple upgrades of the same app: " ) + app_id + ' (instance n° ' + number + ' ) ' )
current_app_dict = app_info ( app_id , instance = number , raw = True )
new_app_dict = app_info ( app_id , raw = True )
if file :
manifest = _extract_app_from_file ( file )
elif url :
manifest = _fetch_app_from_git ( url )
2013-06-06 14:00:30 +02:00
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 ' ] ) ) :
2013-06-06 13:15:38 +02:00
manifest = _fetch_app_from_git ( app_id )
else :
continue
is_web = lvl ( manifest , ' yunohost ' , ' webapp ' )
app_final_path = apps_path + ' / ' + unique_app_id
script_var_dict = {
' APP_DIR ' : app_tmp_folder ,
' APP_FINAL_DIR ' : app_final_path ,
' APP_ID ' : unique_app_id
}
if is_web :
script_var_dict . update ( {
' APP_DOMAIN ' : current_app_dict [ ' settings ' ] [ ' domain ' ] ,
' APP_PATH ' : current_app_dict [ ' settings ' ] [ ' path ' ]
} )
#########################################
# Install dependencies #
#########################################
if lvl ( manifest , ' dependencies ' ) :
_install_app_dependencies ( manifest [ ' dependencies ' ] )
#########################################
# DB Vars #
#########################################
if lvl ( manifest , ' yunohost ' , ' webapp ' , ' db ' ) :
script_var_dict . update ( {
' DB_USER ' : current_app_dict [ ' settings ' ] [ ' db_user ' ] ,
' DB_PWD ' : current_app_dict [ ' settings ' ] [ ' db_pwd ' ] ,
' DB_NAME ' : current_app_dict [ ' settings ' ] [ ' db_user ' ]
} )
#########################################
# Execute App install script #
#########################################
if lvl ( manifest , ' yunohost ' , ' script_path ' ) :
_exec_app_script ( step = ' upgrade ' , path = app_tmp_folder + ' / ' + manifest [ ' yunohost ' ] [ ' script_path ' ] , var_dict = script_var_dict , app_type = manifest [ ' type ' ] )
#########################################
# Copy files to the right final place #
#########################################
# TMP: Remove old application
if os . path . exists ( app_final_path ) : shutil . rmtree ( app_final_path )
os . system ( ' cp -a " ' + app_tmp_folder + ' " " ' + app_final_path + ' " ' )
if is_web :
os . system ( ' chown -R www-data: " ' + app_final_path + ' " ' )
os . system ( ' service apache2 reload ' )
shutil . rmtree ( app_final_path + manifest [ ' yunohost ' ] [ ' script_path ' ] )
#########################################
# Write App settings #
#########################################
app_setting_path = apps_setting_path + ' / ' + unique_app_id
current_app_dict [ ' settings ' ] [ ' update_time ' ] = int ( time . time ( ) )
with open ( app_setting_path + ' /app_settings.yml ' , ' w ' ) as f :
yaml . safe_dump ( current_app_dict [ ' settings ' ] , f , default_flow_style = False )
win_msg ( _ ( " App setting file updated " ) )
if lvl ( manifest , ' yunohost ' , ' script_path ' ) :
os . system ( ' cp -a " ' + app_tmp_folder + ' / ' + manifest [ ' yunohost ' ] [ ' script_path ' ] + ' " ' + app_setting_path )
shutil . rmtree ( app_tmp_folder )
os . system ( ' mv " ' + app_final_path + ' /manifest.webapp " ' + app_setting_path )
if os . system ( ' chmod 400 -R ' + app_setting_path ) != 0 :
raise YunoHostError ( 22 , _ ( " Error during permission setting " ) )
#########################################
# So much win #
#########################################
2013-06-06 14:00:30 +02:00
upgraded_apps . append ( unique_app_id )
2013-06-06 13:15:38 +02:00
win_msg ( app_id + _ ( " upgraded successfully " ) )
if not upgraded_apps :
raise YunoHostError ( 1 , _ ( " No app to upgrade " ) )
win_msg ( _ ( " Upgrade complete " ) )
2013-03-02 11:19:20 +01:00
2013-03-01 17:46:42 +01:00
def app_install ( app , domain , path = ' / ' , label = None , mode = ' private ' ) :
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 :
mode - - level of privacy of the app ( public | protected | private )
domain
app - - App to install
2013-07-06 12:59:06 +02:00
path
2013-07-06 10:17:16 +02:00
label
2013-02-11 11:14:39 +01:00
"""
2013-03-01 20:03:12 +01:00
# TODO: Virer la règle "default" lemon
# TODO: check path and url_to_(un)protect pattern
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-03-01 23:00:22 +01:00
2013-03-02 11:19:20 +01:00
##########################################
# Check if App location is available #
##########################################
if path [ len ( path ) - 1 : ] != ' / ' :
path = path + ' / '
apps_map = app_map ( )
if lvl ( apps_map , domain , path ) :
raise YunoHostError ( 1 , _ ( " An app is already installed on this location " ) )
if lvl ( apps_map , domain ) :
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 " ) )
2013-03-01 23:00:22 +01:00
##########################################
# Fetch or extract sources #
##########################################
2013-03-01 20:03:12 +01:00
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-03-01 20:03:12 +01:00
#########################################
# Define App ID & path #
#########################################
2013-03-01 19:28:00 +01:00
if not lvl ( manifest , ' yunohost ' , ' uid ' ) or ' __ ' in manifest [ ' yunohost ' ] [ ' uid ' ] :
2013-02-26 23:13:49 +01:00
raise YunoHostError ( 22 , _ ( " App uid is invalid " ) )
2013-02-11 11:14:39 +01:00
2013-06-04 12:26:43 +02:00
is_web = lvl ( manifest , ' yunohost ' , ' webapp ' )
2013-06-07 13:44:23 +02:00
instance_number = _installed_instance_number ( manifest [ ' yunohost ' ] [ ' uid ' ] , last = True ) + 1
2013-02-26 23:13:49 +01:00
if instance_number > 1 :
2013-03-01 19:28:00 +01:00
if not lvl ( manifest , ' yunohost ' , ' multi_instance ' ) or not is_true ( manifest [ ' yunohost ' ] [ ' multi_instance ' ] ) :
2013-02-26 23:13:49 +01:00
raise YunoHostError ( 1 , _ ( " App is already installed " ) )
2013-03-02 00:17:45 +01:00
2013-02-26 23:13:49 +01:00
unique_app_id = manifest [ ' yunohost ' ] [ ' uid ' ] + ' __ ' + str ( instance_number )
2013-02-28 21:44:13 +01:00
app_final_path = apps_path + ' / ' + unique_app_id
2013-06-04 12:26:43 +02:00
script_var_dict = {
2013-03-09 19:24:32 +01:00
' APP_DIR ' : app_tmp_folder ,
' APP_FINAL_DIR ' : app_final_path ,
' APP_ID ' : unique_app_id
}
2013-06-04 12:26:43 +02:00
if is_web :
script_var_dict . update ( {
' APP_DOMAIN ' : domain ,
' APP_PATH ' : path
} )
2013-02-26 14:52:13 +01:00
2013-02-11 11:14:39 +01:00
2013-03-01 20:03:12 +01:00
#########################################
# Install dependencies #
#########################################
2013-02-26 23:13:49 +01:00
2013-03-01 20:03:12 +01:00
if lvl ( manifest , ' dependencies ' ) :
_install_app_dependencies ( manifest [ ' dependencies ' ] )
#########################################
# Create and init DB #
#########################################
2013-02-11 11:14:39 +01:00
2013-03-01 20:03:12 +01:00
if lvl ( manifest , ' yunohost ' , ' webapp ' , ' db ' ) :
2013-03-01 23:32:06 +01:00
db_user = random_password ( 10 )
db_pwd = random_password ( 12 )
2013-06-06 13:15:38 +02:00
script_var_dict . update ( {
' DB_USER ' : db_user ,
' DB_PWD ' : db_pwd ,
' DB_NAME ' : db_user
} )
2013-03-01 20:03:12 +01:00
_init_app_db ( db_user , db_pwd , manifest [ ' yunohost ' ] [ ' webapp ' ] [ ' db ' ] )
#########################################
# Execute App install script #
#########################################
2013-02-11 11:14:39 +01:00
2013-03-01 19:28:00 +01:00
if lvl ( manifest , ' yunohost ' , ' script_path ' ) :
2013-02-26 23:13:49 +01:00
_exec_app_script ( step = ' install ' , path = app_tmp_folder + ' / ' + manifest [ ' yunohost ' ] [ ' script_path ' ] , var_dict = script_var_dict , app_type = manifest [ ' type ' ] )
2013-03-01 20:08:37 +01:00
2013-03-01 20:03:12 +01:00
#########################################
# Specifically configure lemon & apache #
#########################################
2013-06-04 12:26:43 +02:00
if is_web :
2013-06-22 13:45:57 +02:00
domain_add ( [ domain ] )
2013-02-28 21:44:13 +01:00
2013-03-01 20:03:12 +01:00
#############
# LemonLDAP #
#############
2013-02-28 21:44:13 +01:00
2013-03-01 19:28:00 +01:00
if mode == ' private ' :
lemon_mode = ' accept '
elif mode == ' protected ' and lvl ( manifest , ' yunohost ' , ' webapp ' , ' access_control ' , ' can_be_protected ' ) and is_true ( manifest [ ' yunohost ' ] [ ' webapp ' ] [ ' access_control ' ] [ ' can_be_protected ' ] ) :
lemon_mode = ' unprotect '
elif mode == ' public ' and lvl ( manifest , ' yunohost ' , ' webapp ' , ' access_control ' , ' can_be_public ' ) and is_true ( manifest [ ' yunohost ' ] [ ' webapp ' ] [ ' access_control ' ] [ ' can_be_public ' ] ) :
lemon_mode = ' skip '
else :
raise YunoHostError ( 22 , _ ( " Invalid privacy mode " ) )
2013-03-01 18:24:37 +01:00
2013-03-02 15:15:57 +01:00
lemon_configuration ( {
( ' locationRules ' , domain , ' (?# ' + unique_app_id + ' Z)^ ' + path ) : lemon_mode
} )
2013-03-01 19:28:00 +01:00
2013-03-01 20:03:12 +01:00
##########
# Apache #
##########
2013-05-30 18:00:17 +02:00
a2_conf_lines = [ ' Alias ' + path + ' ' + app_final_path + manifest [ ' launch_path ' ] ]
if path != ' / ' :
a2_conf_lines . append ( ' Alias ' + path [ : len ( path ) - 1 ] + ' ' + app_final_path + manifest [ ' launch_path ' ] )
2013-03-01 20:03:12 +01:00
2013-07-09 12:22:17 +02:00
a2_conf_lines . append ( ' <Directory ' + app_final_path + ' > ' )
2013-03-01 20:03:12 +01:00
if lvl ( manifest , ' yunohost ' , ' webapp ' , ' language ' ) and manifest [ ' yunohost ' ] [ ' webapp ' ] [ ' language ' ] == ' php ' :
2013-03-02 19:00:44 +01:00
for line in open ( a2_template_path + ' /php.conf ' ) : a2_conf_lines . append ( line . rstrip ( ) )
2013-03-01 20:03:12 +01:00
2013-07-09 12:22:17 +02:00
a2_conf_lines . append ( ' </Directory> ' )
2013-03-01 23:00:22 +01:00
with open ( a2_settings_path + ' / ' + domain + ' .d/ ' + unique_app_id + ' .app.conf ' , ' w ' ) as a2_conf :
2013-03-01 20:03:12 +01:00
for line in a2_conf_lines :
2013-03-01 23:00:22 +01:00
a2_conf . write ( line + ' \n ' )
2013-03-01 20:03:12 +01:00
#########################################
# Copy files to the right final place #
#########################################
2013-02-11 11:14:39 +01:00
2013-02-26 18:31:24 +01:00
try : os . listdir ( apps_path )
except OSError : os . makedirs ( apps_path )
2013-02-11 11:14:39 +01:00
2013-02-26 18:31:24 +01:00
# TMP: Remove old application
if os . path . exists ( app_final_path ) : shutil . rmtree ( app_final_path )
2013-02-26 23:13:49 +01:00
2013-02-26 18:31:24 +01:00
os . system ( ' cp -a " ' + app_tmp_folder + ' " " ' + app_final_path + ' " ' )
2013-03-01 20:03:12 +01:00
2013-06-04 12:26:43 +02:00
if is_web :
2013-06-06 13:15:38 +02:00
os . system ( ' chown -R www-data: " ' + app_final_path + ' " ' )
2013-03-01 20:03:12 +01:00
os . system ( ' service apache2 reload ' )
2013-02-26 18:31:24 +01:00
shutil . rmtree ( app_final_path + manifest [ ' yunohost ' ] [ ' script_path ' ] )
2013-02-11 11:14:39 +01:00
2013-02-26 23:13:49 +01:00
2013-03-01 20:03:12 +01:00
#########################################
# Write App settings #
#########################################
app_setting_path = apps_setting_path + ' / ' + unique_app_id
2013-02-26 23:13:49 +01:00
# TMP: Remove old settings
if os . path . exists ( app_setting_path ) : shutil . rmtree ( app_setting_path )
os . makedirs ( app_setting_path )
2013-06-04 12:26:43 +02:00
yaml_dict = {
2013-06-06 13:15:38 +02:00
' uid ' : manifest [ ' yunohost ' ] [ ' uid ' ] ,
2013-06-04 12:26:43 +02:00
' instance ' : instance_number ,
' install_time ' : int ( time . time ( ) ) ,
' mode ' : mode ,
}
if is_web :
yaml_dict . update ( {
2013-02-26 23:13:49 +01:00
' domain ' : domain ,
' path ' : path ,
2013-06-04 12:26:43 +02:00
} )
2013-02-26 23:13:49 +01:00
2013-03-01 19:28:00 +01:00
if lvl ( manifest , ' yunohost ' , ' webapp ' , ' db ' ) :
2013-06-04 12:26:43 +02:00
yaml_dict . update ( {
' db_pwd ' : db_pwd ,
' db_user ' : db_user
} )
2013-02-26 23:13:49 +01:00
if label : yaml_dict [ ' label ' ] = label
else : yaml_dict [ ' label ' ] = manifest [ ' name ' ]
2013-02-11 11:14:39 +01:00
2013-06-04 12:26:43 +02:00
with open ( app_setting_path + ' /app_settings.yml ' , ' w ' ) as f :
yaml . safe_dump ( yaml_dict , f , default_flow_style = False )
win_msg ( _ ( " App setting file created " ) )
2013-02-26 20:36:37 +01:00
2013-03-01 19:28:00 +01:00
if lvl ( manifest , ' yunohost ' , ' script_path ' ) :
2013-02-26 23:13:49 +01:00
os . system ( ' cp -a " ' + app_tmp_folder + ' / ' + manifest [ ' yunohost ' ] [ ' script_path ' ] + ' " ' + app_setting_path )
2013-02-28 18:22:34 +01:00
2013-02-28 17:58:18 +01:00
shutil . rmtree ( app_tmp_folder )
2013-06-06 13:15:38 +02:00
os . system ( ' mv " ' + app_final_path + ' /manifest.webapp " ' + app_setting_path )
2013-02-26 20:36:37 +01:00
2013-03-01 20:03:12 +01:00
if os . system ( ' chmod 400 -R ' + app_setting_path ) != 0 :
2013-02-26 23:13:49 +01:00
raise YunoHostError ( 22 , _ ( " Error during permission setting " ) )
2013-02-24 17:36:58 +01:00
2013-02-11 11:14:39 +01:00
2013-03-01 20:03:12 +01:00
#########################################
# So much win #
#########################################
win_msg ( _ ( " Installation complete " ) )
2013-06-07 13:44:23 +02:00
def app_remove ( app , instance = [ ] ) :
"""
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-07-06 12:59:06 +02:00
instance - - App instance number to delete
2013-06-07 13:44:23 +02:00
"""
lemon_conf_lines = { }
if not instance :
instance = _installed_instance_number ( app )
for number in instance :
number = str ( number )
unique_app_id = app + ' __ ' + number
app_final_path = apps_path + ' / ' + unique_app_id
app_dict = app_info ( app , instance = number , raw = True )
app_settings = app_dict [ ' settings ' ]
manifest = app_dict [ ' manifest ' ]
is_web = lvl ( manifest , ' yunohost ' , ' webapp ' )
has_db = lvl ( manifest , ' yunohost ' , ' webapp ' , ' db ' )
2013-06-07 14:26:54 +02:00
script_var_dict = {
' APP_DIR ' : apps_path + ' / ' + unique_app_id ,
' APP_ID ' : unique_app_id
}
if lvl ( manifest , ' dependencies ' ) :
2013-06-07 15:30:44 +02:00
#TODO: _remove_app_dependencies(manifest['dependencies'])
2013-06-07 14:26:54 +02:00
pass
if lvl ( manifest , ' yunohost ' , ' script_path ' ) :
_exec_app_script ( step = ' remove ' , path = app_tmp_folder + ' / ' + manifest [ ' yunohost ' ] [ ' script_path ' ] , var_dict = script_var_dict , app_type = manifest [ ' type ' ] )
2013-06-07 13:44:23 +02:00
if is_web :
2013-06-07 15:30:44 +02:00
with open ( lemon_tmp_conf , ' w ' ) as lemon_conf :
2013-06-07 14:26:54 +02:00
hash = " $tmp-> { ' locationRules ' }-> { ' " + app_settings [ ' domain ' ] + " ' } "
lemon_conf . write ( " foreach my $key (keys % { " + hash + " }) { delete " + hash + " { $key} if $key =~ / " + app_settings [ ' uid ' ] + " /; } " + ' \n ' )
os . system ( ' /usr/share/lemonldap-ng/bin/lmYnhMoulinette ' )
2013-06-07 13:44:23 +02:00
try :
os . remove ( a2_settings_path + ' / ' + app_settings [ ' domain ' ] + ' .d/ ' + unique_app_id + ' .app.conf ' )
except OSError :
pass
if has_db :
mysql_root_pwd = open ( ' /etc/yunohost/mysql ' ) . read ( ) . rstrip ( )
mysql_command = ' mysql -u root -p ' + mysql_root_pwd + ' -e " REVOKE ALL PRIVILEGES ON ' + app_settings [ ' db_user ' ] + ' .* FROM \' ' + app_settings [ ' db_user ' ] + ' \' @localhost ; DROP USER \' ' + app_settings [ ' db_user ' ] + ' \' @localhost; DROP DATABASE ' + app_settings [ ' db_user ' ] + ' ; " '
os . system ( mysql_command )
try :
shutil . rmtree ( apps_setting_path + ' / ' + unique_app_id )
shutil . rmtree ( apps_path + ' / ' + unique_app_id )
except OSError :
pass
win_msg ( _ ( " App removed: " ) + unique_app_id )
if is_web :
lemon_configuration ( lemon_conf_lines )
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
"""
if not isinstance ( users , list ) : users = [ users ]
if not isinstance ( apps , list ) : apps = [ apps ]
installed_apps = os . listdir ( apps_setting_path )
lemon_conf_lines = { }
for installed_app in installed_apps :
for app in apps :
if ' __ ' not in app :
app = app + ' __1 '
if app == installed_app :
with open ( apps_setting_path + installed_app + ' /app_settings.yml ' ) as f :
app_settings = yaml . load ( f )
if app_settings [ ' mode ' ] == ' private ' :
if ' allowed_users ' in app_settings :
new_users = app_settings [ ' allowed_users ' ]
else :
new_users = ' '
for allowed_user in users :
2013-03-03 16:07:07 +01:00
if allowed_user not in new_users . split ( ' ' ) :
2013-03-03 18:56:27 +01:00
try :
user_info ( allowed_user )
except YunoHostError :
continue
2013-03-03 16:07:07 +01:00
new_users = new_users + ' ' + allowed_user
2013-03-03 17:45:31 +01:00
app_settings [ ' allowed_users ' ] = new_users . strip ( )
2013-03-03 16:07:07 +01:00
with open ( apps_setting_path + installed_app + ' /app_settings.yml ' , ' w ' ) as f :
yaml . safe_dump ( app_settings , f , default_flow_style = False )
win_msg ( _ ( " App setting file updated " ) )
2013-03-03 15:54:24 +01:00
2013-03-03 17:45:31 +01:00
lemon_conf_lines [ ( ' locationRules ' , app_settings [ ' domain ' ] , ' (?# ' + installed_app + ' Z)^ ' + app_settings [ ' path ' ] ) ] = ' grep( /^$uid$/, qw( ' + new_users . strip ( ) + ' )) '
lemon_configuration ( lemon_conf_lines )
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
"""
if not isinstance ( users , list ) : users = [ users ]
if not isinstance ( apps , list ) : apps = [ apps ]
installed_apps = os . listdir ( apps_setting_path )
lemon_conf_lines = { }
for installed_app in installed_apps :
for app in apps :
new_users = ' '
if ' __ ' not in app :
app = app + ' __1 '
if app == installed_app :
with open ( apps_setting_path + installed_app + ' /app_settings.yml ' ) as f :
app_settings = yaml . load ( f )
if app_settings [ ' mode ' ] == ' private ' :
if ' allowed_users ' in app_settings :
for allowed_user in app_settings [ ' allowed_users ' ] . split ( ' ' ) :
if allowed_user not in users :
new_users = new_users + ' ' + allowed_user
app_settings [ ' allowed_users ' ] = new_users . strip ( )
with open ( apps_setting_path + installed_app + ' /app_settings.yml ' , ' w ' ) as f :
yaml . safe_dump ( app_settings , f , default_flow_style = False )
win_msg ( _ ( " App setting file updated " ) )
lemon_conf_lines [ ( ' locationRules ' , app_settings [ ' domain ' ] , ' (?# ' + installed_app + ' Z)^ ' + app_settings [ ' path ' ] ) ] = ' grep( /^$uid$/, qw( ' + new_users . strip ( ) + ' )) '
2013-03-03 15:54:24 +01:00
lemon_configuration ( lemon_conf_lines )
2013-03-01 20:03:12 +01:00
2013-03-02 12:09:22 +01:00
def _extract_app_from_file ( path ) :
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-02-25 22:23:32 +01:00
Returns :
Dict manifest
"""
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-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 )
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 :
with open ( app_tmp_folder + ' /manifest.webapp ' ) as json_manifest :
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 ) :
git_result = os . system ( ' git clone ' + app + ' ' + app_tmp_folder )
git_result_2 = 0
try :
with open ( app_tmp_folder + ' /manifest.webapp ' ) as json_manifest :
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_tmp_folder = install_tmp + ' / ' + app
if os . path . exists ( app_tmp_folder ) : shutil . rmtree ( app_tmp_folder )
2013-02-25 22:23:32 +01:00
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 " ) )
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-02-26 14:52:13 +01:00
def _install_app_dependencies ( dep_dict ) :
2013-02-25 22:23:32 +01:00
"""
Install debian , npm , gem , pip and pear dependencies of the app
Keyword arguments :
dep_dict - - Dict of dependencies from the manifest
"""
if ( ' debian ' in dep_dict ) and ( len ( dep_dict [ ' debian ' ] ) > 0 ) :
#os.system('apt-get update')
2013-05-30 17:47:38 +02:00
if os . system ( ' apt-get install -y " ' + ' " " ' . join ( dep_dict [ ' debian ' ] ) + ' " ' ) != 0 :
2013-02-25 22:23:32 +01:00
raise YunoHostError ( 1 , _ ( " Dependency installation failed: " ) + dependency )
# TODO: Install npm, pip, gem and pear dependencies
2013-02-26 14:52:13 +01:00
win_msg ( _ ( " Dependencies installed " ) )
2013-02-25 22:23:32 +01:00
2013-02-26 14:52:13 +01:00
def _init_app_db ( db_user , db_pwd , db_dict ) :
2013-02-25 22:23:32 +01:00
"""
Create database and initialize it with optionnal attached script
Keyword arguments :
db_user - - Name of the DB user ( also used as database name )
db_pwd - - Password for the user
db_dict - - Dict of DB parameters from the manifest
"""
# Need MySQL DB ?
2013-03-01 19:28:00 +01:00
if lvl ( db_dict , ' has_mysql_db ' ) and is_true ( db_dict [ ' has_mysql_db ' ] ) :
2013-03-01 23:00:22 +01:00
mysql_root_pwd = open ( ' /etc/yunohost/mysql ' ) . read ( ) . rstrip ( )
2013-02-25 22:23:32 +01:00
mysql_command = ' mysql -u root -p ' + mysql_root_pwd + ' -e " CREATE DATABASE ' + db_user + ' ; GRANT ALL PRIVILEGES ON ' + db_user + ' .* TO \' ' + db_user + ' \' @localhost IDENTIFIED BY \' ' + db_pwd + ' \' ; " '
if os . system ( mysql_command ) != 0 :
raise YunoHostError ( 1 , _ ( " MySQL DB creation failed " ) )
2013-03-01 19:28:00 +01:00
if lvl ( db_dict , ' mysql_init_script ' ) :
2013-02-25 22:23:32 +01:00
if os . system ( ' mysql -u ' + db_user + ' -p ' + db_pwd + ' ' + db_user + ' < ' + app_tmp_folder + db_dict [ ' mysql_init_script ' ] + ' ; ' ) != 0 :
raise YunoHostError ( 1 , _ ( " MySQL DB init failed " ) )
# TODO: PgSQL/MongoDB ?
2013-02-26 14:52:13 +01:00
win_msg ( _ ( " Database initiliazed " ) )
def _exec_app_script ( step , path , var_dict , app_type ) :
"""
Execute step user script
Keyword arguments :
step - - Name of the script to call regarding the current step ( e . g . install | upgrade | remove | etc . )
path - - Absolute path of the script ' s directory
var_dict - - Dictionnary of environnement variable to pass to the script
app_type - - Decides whether to execute as root or as yunohost - app user ( e . g . web | privileged | certified )
"""
scripts = [ step , step + ' .sh ' , step + ' .py ' ]
for script in scripts :
script_path = path + ' / ' + script
if os . path . exists ( script_path ) :
st = os . stat ( script_path )
os . chmod ( script_path , st . st_mode | stat . S_IEXEC )
if app_type == ' privileged ' or app_type == ' certified ' :
user = ' root '
else :
user = ' yunohost-app '
os . system ( ' chown -R ' + user + ' : ' + app_tmp_folder )
env_vars = ' '
for key , value in var_dict . items ( ) :
env_vars = env_vars + key + " = ' " + value + " ' "
command = ' su - ' + user + ' -c " ' + env_vars + ' sh ' + path + ' / ' + script + ' " '
if os . system ( command ) == 0 :
win_msg ( _ ( " Script executed: " ) + script )
else :
raise YunoHostError ( 1 , _ ( " Script execution failed: " ) + script )
break
2013-02-26 18:31:24 +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 :
app - - uid 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 :
if ' __ ' in installed_app :
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