Welcome    Usage    Browse    Find CID    Search     Log in

cM API Documentation

cm.py

Go to the documentation of this file.
00001 #
00002 # Collective Mind
00003 #
00004 # See cM LICENSE.txt for licensing details.
00005 # See cM Copyright.txt for copyright details.
00006 #
00007 # Developer(s): (C) Grigori Fursin, started on 2011.09
00008 #
00009 
00010 import imp
00011 import sys
00012 import os
00013 import json
00014 import uuid
00015 import random
00016 import re
00017 import datetime
00018 import string
00019 import urllib2
00020 import json
00021 import shutil
00022 import tempfile
00023 import subprocess
00024 import time
00025 import signal
00026 import hashlib
00027 import cgi
00028 import base64
00029 import copy
00030 import getpass
00031 import zipfile
00032 import shlex
00033 import platform
00034 
00035 # Core variables that has to be defined here for bootstrapping.
00036 # All other paramaters can be moved to configuration file so
00037 # that cM library written in other languages can easily reuse them.
00038 # We do not use classes again to be able to reuse functionality
00039 # in other languages.
00040 
00041 env_cm_root='CM_ROOT'
00042 env_cm_bin='CM_BIN'
00043 env_cm_tmp='CM_TMP'
00044 env_cm_cfg='CM_DEFAULT_CFG'
00045 env_cm_cfg_path='CM_DEFAULT_CFG_PATH'
00046 env_cm_default_repo='CM_DEFAULT_REPO'
00047 env_cm_local_repo='CM_LOCAL_REPO'
00048 env_cm_user_uoa='CM_USER_UOA'
00049 env_cm_user_password='CM_USER_PASSWORD'
00050 env_cm_user_password1='CM_USER_PASSWORD1'
00051 env_cm_remote_repo_proxy_http='CM_REMOTE_REPO_PROXY_HTTP'
00052 env_cm_remote_repo_proxy_https='CM_REMOTE_REPO_PROXY_HTTPS'
00053 
00054 var_cm_info='.cm'
00055 var_cm_data='data.json'
00056 var_cm_cfg_local='local'
00057 var_cm_cfg_default='default'
00058 
00059 cm_module_default='core'
00060 cm_module_default_access='access'
00061 cm_action_default='default'
00062 cm_unparsed_sep='--'
00063 
00064 cm_json_with_text_sep='*** ### --- CM JSON SEPARATOR --- ### ***'
00065 
00066 ini={
00067      # This variable describes initialization of cM framework
00068      # -1 - not initalized
00069      # 0 - initialized 
00070      # 1 - error during initialization
00071      'init':-1,
00072      'loaded_module_by_path':{},
00073      'loaded_module_by_path_last_modification':{},
00074      'loaded_data_by_path':{},
00075      'loaded_data_by_path_last_modification':{},
00076      'pcfg':'',
00077      'cfg':{},
00078      'dcfg':{},
00079      'path':'',
00080      'web':'no',
00081      'web_auth':{},
00082      'web_style':{},
00083      'remote_repo':{}
00084     }
00085 
00086 # Local variables to simplify referencing
00087 cm_kernel=None
00088 
00089 print_on=True
00090 
00091 # ============================================================================
00092 def init(i):
00093 
00094     """
00095     Module init
00096 
00097     Input:  {}
00098 
00099     Output: {
00100               cm_return       - return code = 0, if successful
00101               (cm_error)      - error text if return code > 0
00102             }
00103     """
00104 
00105     global cm_kernel, ini
00106 
00107     # Save first kernel (bootsrapping)
00108     if cm_kernel==None:
00109        cm_kernel=sys.modules[__name__]
00110 
00111     # Load configuration for boostrapping (first check direct cfg, then check local and default entries)
00112     if not env_cm_cfg in os.environ.keys() and not env_cm_cfg_path in os.environ.keys():
00113        return {'cm_return':1, 'cm_error':'neither of environment variables '+env_cm_cfg+' and '+env_cm_cfg_path+' are defined!'}
00114 
00115     pcfg=''
00116     if env_cm_cfg in os.environ.keys() and os.environ[env_cm_cfg]!='':
00117        pcfg=os.environ[env_cm_cfg]
00118     elif env_cm_cfg_path in os.environ.keys() and os.environ[env_cm_cfg_path]!='':
00119        p=os.path.join(os.environ[env_cm_cfg_path], var_cm_cfg_local, var_cm_info, var_cm_data)
00120        if os.path.isfile(p): pcfg=p
00121        else:
00122           p=os.path.join(os.environ[env_cm_cfg_path], var_cm_cfg_default, var_cm_info, var_cm_data)
00123           if os.path.isfile(p): pcfg=p
00124 
00125     if pcfg=='':
00126        return {'cm_return':1, 'cm_error':'can\'t detect default kernel config file'}
00127 
00128     r=load_json_file({'cm_filename':pcfg})
00129     if r['cm_return']>0: return r
00130 
00131     ini['dcfg']=r['cm_array']
00132     ini['pcfg']=pcfg
00133 
00134     # Check cM root
00135     if env_cm_root in os.environ.keys():
00136        ini[env_cm_root]=os.environ[env_cm_root]
00137     else:
00138        return {'cm_return':1, 'cm_error':'environment variable '+env_cm_root+' is not defined!'}
00139 
00140     # Check cM root
00141     if env_cm_bin in os.environ.keys():
00142        ini[env_cm_bin]=os.environ[env_cm_bin]
00143     else:
00144        ini[env_cm_bin]=os.path.normpath(os.path.join(ini[env_cm_root], ini['dcfg']['bin_dir']))
00145 
00146     # Check cM root
00147     if env_cm_tmp in os.environ.keys():
00148        ini[env_cm_tmp]=os.environ[env_cm_tmp]
00149     else:
00150        return {'cm_return':1, 'cm_error':'environment variable '+env_cm_tmp+' is not defined!'}
00151 
00152     # Check local repository
00153     if env_cm_local_repo in os.environ.keys():
00154        ini['cm_local_repo']=os.path.normpath(os.environ[env_cm_local_repo])
00155 
00156     # Check cM default repo
00157     if env_cm_default_repo in os.environ.keys():
00158        ini['cm_default_repo']=os.path.normpath(os.environ[env_cm_default_repo])
00159     else:
00160        ini['cm_default_repo']=os.path.normpath(os.path.join(ini[env_cm_root], ini['dcfg']['cm_default_repo_path'], ini['dcfg']['cmr_ext']))
00161 
00162     return {'cm_return':0}
00163 
00164 # ============================================================================
00165 def is_windows():
00166     """
00167     Is Windows?
00168 
00169     Input: None
00170 
00171     Output: True if Windows
00172     """
00173 
00174     win=False
00175 
00176     if platform.system().lower().startswith('win'):
00177        win=True
00178 
00179     return win
00180 
00181 # ============================================================================
00182 def load_data(i):
00183     """
00184     Load and cache cM data
00185 
00186     Input:  {
00187               cm_module_uoa       - UOA of the module
00188               cm_data_uoa         - UOA of the data
00189               (cm_path)           - path to repository  
00190               (cm_path_array)     - array of paths to search
00191               (lock_acquire)      - if 'yes', acquire lock
00192               (lock_retry_num)    - number of retries to acquire lock (default=6)
00193               (lock_retry_delay)  - delay in sec before retrying to acquire lock (default=11)
00194               (lock_expire)       - for how long to acquire lock (secs) 
00195               (unlock_uid)        - unlock data (FGG note: I changed lock_uid to unlock_uid 
00196                                                  not to interfere with update locking)
00197               (cm_admin)          - if 'yes', override access control - only internal use,
00198                                     can not be used during web access (explicitly removed)
00199             }
00200 
00201     Output: {
00202               cm_return           - return code = 0, if successful
00203               (cm_error)          - error text if return code > 0
00204               cm_data_obj         - cM data object
00205               cm_path             - path to data entry
00206               cm_path_module      - path to module entry with this entry
00207               cm_path_repo        - path to the repository of this entry
00208               cm_uid              - uid (from UOA)
00209               cm_alias            - alias (from UOA)
00210               cm_uoa              - cm_data_uoa (alias or uid if alias=='')
00211               cm_module_uoa       - module UOA 
00212               cm_module_uid       - module UID
00213               cm_display_as_alias - taken from the data
00214               cm_display_html     - prepare display: "display as alias" or <I>alias</I> or UID
00215               (lock_uid)          - UID of the aquired lock
00216             }
00217     """
00218 
00219     if 'cm_module_uoa' not in i or i['cm_module_uoa']=='':
00220        return {'cm_return':1, 'cm_error':'parameter "cm_module_uoa" is not defined'}
00221 
00222     if 'cm_data_uoa' not in i or i['cm_data_uoa']=='':
00223        return {'cm_return':1, 'cm_error':'parameter "cm_data_uoa" is not defined'}
00224 
00225     # Find path to data
00226     if 'cm_path_array' in i: path_repo=i['cm_path_array']
00227     else: path_repo=[]
00228 
00229     if 'cm_path' in i and i['cm_path']!='': path_repo.append(i['cm_path'])
00230 
00231     if len(path_repo)==0:
00232        r=find_path_to_work_repositories({})
00233        if r['cm_return']>0: return r
00234        for x in r['cm_array']:
00235            if x not in path_repo: path_repo.append(x)
00236 
00237     module_uoa=i['cm_module_uoa']
00238     module_uid=''
00239     uoa=i['cm_data_uoa']
00240 
00241     # Continue
00242     found=False
00243     for p in path_repo:
00244         y=find_path_to_entry({'cm_path':p, 'cm_data_uoa':i['cm_module_uoa']})
00245         if y['cm_return']>0: return y
00246         elif y['cm_return']==0:
00247            module_uoa=y['cm_uoa']
00248            module_uid=y['cm_uid']
00249            pm=y['cm_path']
00250            y1=find_path_to_entry({'cm_path':pm, 'cm_data_uoa':i['cm_data_uoa']})
00251            if y1['cm_return']>0: return y1
00252            elif y1['cm_return']==0:
00253               found=True
00254               uid=y1['cm_uid']
00255               alias=y1['cm_alias']
00256               uoa=y1['cm_uoa']
00257               if alias!='': uoa=alias
00258               break
00259 
00260     if not found:
00261        return {'cm_return':16, 'cm_error':'can\'t find path to data "'+module_uoa+':'+uoa+'"'}
00262 
00263     pd=y1['cm_path']
00264 
00265     p0=os.path.join(pd, ini['dcfg']['dcm'])
00266     p1=os.path.join(p0, ini['dcfg']['fconfig'])
00267     p5=os.path.join(p0, ini['dcfg']['flock'])
00268 
00269     # Check if acquire coarse-grain lock or release it
00270     locks={}
00271     if i.get('lock_acquire','')=='yes' or i.get('unlock_uid','')!='':
00272 
00273        luid=''
00274        if os.path.isfile(p5):
00275 
00276           try:
00277              f=file(p5)
00278              luid=f.readline().strip()
00279              exp=f.readline().strip()
00280              f.close()
00281           except Exception as e:
00282              return {'cm_return':1, 'cm_error':'problem reading locking file'}
00283 
00284           # Check if lock expired
00285           if i.get('lock_acquire','')=='yes':
00286              # Retry if locked
00287              retry=int(ini['dcfg'].get('flock_retry_num','6'))
00288              if i.get('lock_retry_num','')!='': retry=int(i['lock_retry_num'])
00289              retryd=float(ini['dcfg'].get('flock_retry_delay','11'))
00290              if i.get('lock_retry_delay','')!='': retry=float(i['lock_retry_delay'])
00291 
00292              if exp=='': exp=ini['dcfg'].get('flock_expire_max','60')
00293 
00294              dt=os.path.getmtime(p5)+float(exp)-time.time()
00295              if dt>0: 
00296                 while retry>0 and os.path.isfile(p5) and dt>0:
00297                    retry-=1
00298                    time.sleep(retryd)
00299                    if os.path.isfile(p5): dt=os.path.getmtime(p5)+float(exp)-time.time()
00300 
00301                 if retry==0 or os.path.isfile(p5):
00302                    return {'cm_return':32, 'cm_error':'data is locked'}
00303 
00304              luid=''
00305              if os.path.isfile(p5): os.remove(p5)
00306 
00307        if luid!='' and i.get('unlock_uid','')!='':
00308           if luid!=i['unlock_uid']:
00309              return {'cm_return':32, 'cm_error': 'data is locked with another UID'}
00310           luid=''
00311           os.remove(p5)
00312 
00313        if i.get('lock_acquire','')=='yes':
00314           # (Re)acquire lock
00315           r=gen_uid({})
00316           if r['cm_return']>0: return r
00317           luid=r['cm_uid']
00318           exp1=i.get('lock_expire','')
00319           if exp1=='' or float(exp1)<1: exp1=ini['dcfg'].get('flock_expire_max','60')
00320 
00321           f=open(p5,'w')
00322           f.write(luid+'\n')
00323           f.write(exp1+'\n')
00324           f.close()
00325 
00326           locks['lock_uid']=luid
00327 
00328     # Check if data has been already loaded
00329     cached=None
00330     cm_display_as_alias=''
00331     cm_display_html=''
00332     if pd in ini['loaded_data_by_path'] and ini['loaded_data_by_path_last_modification'][pd]==os.path.getmtime(p1):
00333        # Code already loaded 
00334        cached=ini['loaded_data_by_path'][pd]
00335        # FGG HACK - sometimes on intensive updates (test)
00336        # path changes. Here I explicitly change it
00337        cached['cm_path']=pd
00338        xcfg=cached['cm_data_obj']['cfg']
00339     else:
00340        # Load config file
00341        r1=load_json_file({'cm_filename':p1})
00342        if r1['cm_return']>0: return r1
00343        xcfg=r1['cm_array']
00344        if 'cm_display_as_alias' in xcfg and xcfg['cm_display_as_alias']!='': 
00345            cm_display_as_alias=xcfg['cm_display_as_alias']
00346            cm_display_html=cm_display_as_alias[0].upper()+cm_display_as_alias[1:]
00347        elif alias!='':
00348            cm_display_html=alias
00349        else:
00350            cm_display_html=uid
00351 
00352     # Check fine-grain access
00353     ac=ini['dcfg'].get('access_control',{})
00354     if i.get('cm_admin','')!='yes' and ini['web']=='yes' and ac.get('use','')=='yes':
00355        rx=access_control({'dcfg':xcfg, 'module_uoa':module_uoa, \
00356                           'module_uid':module_uid, 'data_uoa':uoa, 'key':'read'})
00357        if rx['cm_return']>0: return rx
00358 
00359     # If cached, quit
00360     if cached!=None: 
00361        cached.update(locks)
00362        return cached
00363 
00364     # Data object
00365     d={}
00366     d['cm_path']=pd
00367     d['cfg']=xcfg
00368 
00369 
00370     # Cache module
00371     o={'cm_return':0, 'cm_data_obj':d, \
00372                       'cm_path':pd, \
00373                       'cm_path_module':pm, \
00374                       'cm_path_repo':p, \
00375                       'cm_uid':uid, \
00376                       'cm_alias':alias, \
00377                       'cm_uoa':uoa, \
00378                       'cm_module_uoa':module_uoa, \
00379                       'cm_module_uid':module_uid, \
00380                       'cm_display_as_alias':cm_display_as_alias, \
00381                       'cm_display_html':cm_display_html}
00382     ini['loaded_data_by_path'][pd]=o
00383     # Cache time of change of json description
00384     ini['loaded_data_by_path_last_modification'][pd]=os.path.getmtime(p1)
00385 
00386     # Update info about locks
00387     o.update(locks)
00388 
00389     return o
00390 
00391 # ============================================================================
00392 def load_module(i):
00393     """
00394     Load and cache cM module
00395 
00396     Input:  {
00397               cm_module_uoa - UOA of the module
00398               (cm_path)
00399             }
00400 
00401     Output: {
00402               cm_return       - return code = 0, if successful
00403               cm_code         - python code object
00404               external_module - if 'yes', module is external
00405               cm_path         - path to data entry
00406               cm_uid          - uid (from UOA)
00407               cm_alias        - alias (from UOA)
00408 
00409             }
00410     """
00411 
00412     if 'cm_module_uoa' not in i:
00413        return {'cm_return':1, 'cm_error':'parameter "cm_module_uoa" is not defined'}
00414 
00415     external=False
00416     if i['cm_module_uoa'].startswith('cme.'): external=True
00417 
00418     if external:
00419        # Module in current directory (or in search path)
00420        p2=''
00421        p3=i['cm_module_uoa']
00422        if p3.endswith('.py'): p3=p3[:-3]
00423 
00424     else:
00425        # Find path to module
00426        ii={'cm_module_uoa':ini['dcfg']['cmr_module'],
00427            'cm_data_uoa':i['cm_module_uoa']}
00428        if 'cm_path' in i: ii.update({'cm_path':i['cm_path']})
00429 
00430        r=load_data(ii)
00431        if r['cm_return']>0: return r
00432        elif r['cm_return']<0: 
00433           return {'cm_return':16, 'cm_error':'can\'t find path to module "'+i['cm_module_uoa']+'"'}
00434        path=r['cm_path']
00435        uid=r['cm_uid']
00436        alias=r['cm_alias']
00437 
00438        p1=os.path.join(path, ini['dcfg']['dcm'], ini['dcfg']['fconfig'])
00439 
00440        # Check if code has been already loaded
00441        if path in ini['loaded_module_by_path'] and ini['loaded_module_by_path_last_modification'][path]==os.path.getmtime(p1):
00442           # Code already loaded 
00443           return ini['loaded_module_by_path'][path]
00444 
00445        # Load config file
00446        r=load_json_file({'cm_filename':p1})
00447        if r['cm_return']>0: return r
00448        xcfg=r['cm_array']
00449 
00450        # Load code
00451        subdir=xcfg['cm_module_py_dir']
00452        p2=os.path.join(path, subdir)
00453        p3=xcfg['cm_module_py_ext']
00454 
00455     r=load_code({'cm_path':p2, 'cm_code_name':p3})
00456     if r['cm_return']>0: return r
00457     c=r['code']
00458 
00459     # FGG: I move various initializations here to simplify kernel and modules
00460     c.cm_kernel=cm_kernel
00461 
00462     if not external:
00463        c.ini['cm_code']=c
00464        c.ini['cm_module_uoa']=i['cm_module_uoa']
00465        c.ini['cm_module_uid']=uid
00466        c.ini['cm_module_alias']=alias
00467        c.ini['cfg']=xcfg
00468        c.ini['path']=path
00469 
00470        r=c.init(i)
00471        if r['cm_return']>0: return r
00472 
00473        # Cache module
00474        o={'cm_return':0, 'cm_code':c, 'cm_path':path, 'cm_uid':uid, 'cm_alias':alias}
00475        ini['loaded_module_by_path'][path]=o
00476        # Cache time of change of json description
00477        ini['loaded_module_by_path_last_modification'][path]=os.path.getmtime(p1)
00478     else:
00479        o={'cm_return':0, 'cm_code':c, 'external_module':'yes'}   
00480 
00481     return o
00482 
00483 # ============================================================================
00484 def load_code(i):
00485     """
00486     Load python code
00487 
00488     Input:  {
00489               cm_path      - path
00490               cm_code_name - name of the python native module
00491             }
00492 
00493     Output: {
00494               cm_return  - return code = 0, if successful
00495               code       - python code object
00496             }
00497     """
00498 
00499     # Load code
00500     try:
00501        x=imp.find_module(i['cm_code_name'], [i['cm_path']])
00502     except ImportError as e:
00503        return {'cm_return':1, 'cm_error':'can\'t find code (path='+i['cm_path']+', name='+i['cm_code_name']+', err='+format(e)+')'}
00504 
00505     # Generate uid for the run-time extension of the loaded module 
00506     # otherwise modules with the same extension (key.py for example) 
00507     # will be reloaded ...
00508 
00509     r=gen_uid({})
00510     if r['cm_return']>0: return r
00511     ruid='runtime-'+r['cm_uid']
00512 
00513     try:
00514        c=imp.load_module(ruid, x[0], x[1], x[2])
00515     except ImportError as e:
00516        return {'cm_return':1, 'cm_error':'can\'t load code (path='+i['cm_path']+', name='+i['cm_code_name']+', err='+format(e)+')'}
00517 
00518     x[0].close()
00519 
00520     return {'cm_return':0, 'code':c}
00521 
00522 # ============================================================================
00523 def gen_uid(i):
00524     """
00525     Generate cM UID
00526 
00527     Input:  {}
00528 
00529     Output: {
00530               cm_return - return code = 0, if successful
00531               cm_uid    - cM UID in string format (16 characters 0..9,a..f)
00532             }
00533     """
00534 
00535     uid=str(uuid.uuid4().hex)
00536 
00537     if len(uid)!=32:
00538        return {'cm_return':1, 'cm_error':'problem generating UID : len='+str(len(uid))+' !=32'}
00539 
00540     random.seed
00541 
00542     x=random.randrange(0,16)
00543     return {'cm_return':0, 'cm_uid':uid[x:x+16]}
00544 
00545 # ============================================================================
00546 def is_uid(str):
00547     """
00548     Check if string is cM UID
00549 
00550     Input: string to check
00551 
00552     Output: True if UID, otherwise False
00553     """
00554 
00555     if len(str)!=16:
00556        return False
00557 
00558     pattern = r'[^\.a-f0-9]'
00559     if re.search(pattern, str.lower()):
00560         return False
00561 
00562     return True
00563 
00564 # ============================================================================
00565 def cid2array(i):
00566     """
00567     Convert cid to array
00568 
00569     Input:  {
00570               cid      - in format (REPO_UOA:)KEY_UOA:DATA_UOA
00571               (prefix) - add prefic to output variables (<prefix>_repo_uoa:...)
00572             }
00573 
00574     Output: {
00575               cm_return - return code = 0 if successful
00576               cm_array  - array
00577             }
00578     """
00579 
00580     prefix='cm'
00581     if 'prefix' in i and i['prefix']!='': prefix=i['prefix']
00582 
00583     array={}
00584     if 'cid' in i and i['cid']!='':
00585         id=i['cid'].split(":")
00586         if (len(id)<2): 
00587            return {'cm_return':1, 'cm_error':'unknown cid format'}
00588         elif (len(id)<3):
00589             array[prefix+'_module_uoa']=id[0]
00590             array[prefix+'_data_uoa']=id[1]
00591         elif (len(id)<4):
00592             array[prefix+'_repo_uoa']=id[0]
00593             array[prefix+'_module_uoa']=id[1]
00594             array[prefix+'_data_uoa']=id[2]
00595     return {'cm_return':0, 'cm_array':array}
00596 
00597 
00598 # ============================================================================
00599 def web_print_array_for_debug(i):
00600     """
00601     Print array for debug purposes (use <pre> and <div align="left")
00602 
00603     Input:  {
00604               some array
00605             }
00606 
00607     Output: {
00608               cm_return - 0
00609             }
00610     """
00611 
00612     print '<pre><div align="left">'+json.dumps(i, indent=2, sort_keys=True)+'</div></pre>'
00613 
00614 
00615     return {'cm_return':0}
00616 
00617 # ============================================================================
00618 def find_path_to_entry(i):
00619     """
00620     Find path to an UOA entry (check UID or alias).
00621 
00622     Input:  {
00623               cm_path     - path to a repository
00624               cm_data_uoa - data UOA
00625             }
00626 
00627     Output: {
00628               cm_return      - return code =  0 if found path to entry
00629                                            = -1 if not found path to entry
00630                                            >  0 if error
00631 
00632               cm_path_orig   - i['cm_path']
00633               cm_path        - path to data entry
00634               cm_uid         - uid (from UOA)
00635               cm_alias       - alias (from UOA)
00636               cm_uoa         - alias or uid, if alias==''
00637             }
00638     """
00639 
00640     if ('cm_path' not in i):
00641        return {'cm_return':1, 'cm_error':'parameter "'+'cm_path'+'" is not defined'}
00642     if ('cm_data_uoa' not in i):
00643        return {'cm_return':1, 'cm_error':'parameter "cm_data_uoa" is not defined'}
00644 
00645     # Disambiguate UOA
00646     alias=''
00647     if is_uid(i['cm_data_uoa']):
00648        # If UID
00649        uid=i['cm_data_uoa']
00650 
00651        # Check if alias exists
00652        p1=os.path.join(i['cm_path'], ini['dcfg']['dcm'], ini['dcfg']['falias-u'] + uid)
00653        found_alias=False
00654        if os.path.isfile(p1):
00655           try:
00656              f=file(p1)
00657              alias=f.readline().strip()
00658              f.close()
00659              found_alias=True
00660           except Exception as e:
00661              None
00662 
00663        # If alias exist, check directory with alias
00664        if found_alias:
00665           p2=os.path.join(i['cm_path'], alias)
00666           if not os.path.isdir(p2):
00667              #auto-repairment
00668              try:
00669                 os.mkdir(p2)
00670              except Exception as e:
00671                 return {'cm_return':1, 'cm_error':format(e)}
00672 
00673           return {'cm_return':0, 'cm_path':p2, 'cm_uid':uid, 'cm_alias':alias, 'cm_uoa':alias}
00674 
00675        p2=os.path.join(i['cm_path'], uid)
00676        if os.path.isdir(p2):
00677           return {'cm_return':0, 'cm_path':p2, 'cm_uid':uid, 'cm_alias':'', 'cm_uoa':uid}
00678 
00679        return {'cm_return':-1}
00680 
00681     # If alias
00682     alias=i['cm_data_uoa']
00683 
00684     p1=os.path.join(i['cm_path'], alias)
00685     if os.path.isdir(p1):
00686        # Check uid for this alias
00687        p2=os.path.join(i['cm_path'], ini['dcfg']['dcm'], ini['dcfg']['falias-a'] + alias)
00688        try:
00689           f=file(p2)
00690           uid=f.readline().strip()
00691           f.close()
00692        except Exception as e:
00693           return {'cm_return':10, 'cm_error':'inconsistent entry: alias "'+alias+'" exists, but not the uid in file '+p2, 'cm_path_orig':i['cm_path'], 'cm_path':p1, 'cm_alias':alias}
00694 
00695        return {'cm_return':0, 'cm_path_orig': i['cm_path'], 'cm_path':p1, 'cm_uid':uid, 'cm_alias':alias, 'cm_uoa':alias}
00696 
00697     return {'cm_return':-1}
00698 
00699 # ============================================================================
00700 def create_entry(i):
00701     """
00702     Create an UOA entry directory structure.
00703 
00704     Input:  {
00705               cm_path       - path to a repository
00706               (cm_data_uoa) - data UOA
00707               (cm_data_uid) - if cm_data_uoa==alias, we can force a given UID through this variable
00708               (lock_uid)    - UID of the aquired lock
00709             }
00710 
00711     Output: {
00712               cm_return - return code =  0 if created entry
00713                                       = -1 if already exist
00714                                       >  0 if error
00715 
00716               cm_path   - path to data entry
00717               cm_uid    - uid (from UOA)
00718               cm_alias  - alias (from UOA)
00719               cm_uoa    - alias or uid if alias==''
00720             }
00721     """
00722 
00723     if ('cm_path' not in i):
00724        return {'cm_return':1, 'cm_error':'parameter "'+'cm_path'+'" is not defined'}
00725 
00726     # If no cm_data_uoa, generate one
00727     alias=''
00728     uid=''
00729     if ('cm_data_uoa' not in i or i['cm_data_uoa']==''):
00730        if 'cm_data_uid' not in i or i['cm_data_uid']=='':
00731           r=gen_uid({})
00732           if r['cm_return']>0: return r
00733           uid=r['cm_uid']
00734        else:
00735           uid=i['cm_data_uid']
00736 
00737           # Check if already exist
00738           ii={}
00739           ii.update(i)
00740           ii['cm_data_uoa']=uid
00741           r=find_path_to_entry(ii)
00742           if r['cm_return']>0: return r
00743           elif r['cm_return']==0:
00744              r1={}; r1.update(r); r1['cm_return']=-1
00745              return r1
00746 
00747        alias=''
00748     else:
00749        # Check if already exist
00750        r=find_path_to_entry(i)
00751        if r['cm_return']>0: return r
00752        elif r['cm_return']==0:
00753           r1={}; r1.update(r); r1['cm_return']=-1
00754           return r1
00755 
00756        if is_uid(i['cm_data_uoa']):
00757           uid=i['cm_data_uoa']; alias=''
00758        else:
00759           alias=i['cm_data_uoa']
00760           if 'cm_data_uid' in i: uid=i['cm_data_uid']
00761           else: 
00762              r=gen_uid({})
00763              if r['cm_return']>0: return r
00764              uid=r['cm_uid']
00765 
00766     if alias!='':
00767        p=os.path.join(i['cm_path'], alias)
00768     else:
00769        p=os.path.join(i['cm_path'], uid)
00770 
00771     # Check alias disamibugation
00772     if alias!='':
00773        p1=os.path.join(i['cm_path'], ini['dcfg']['dcm'])
00774        if not os.path.isdir(p1):
00775           # Create .cm directory
00776           try:
00777              os.mkdir(p1)
00778           except Exception as e:
00779              return {'cm_return':1, 'cm_error':format(e)}
00780 
00781        # Check if alias->uid exist
00782        p3=os.path.join(p1, ini['dcfg']['falias-a'] + alias)
00783        if os.path.isfile(p3):
00784           try:
00785              fx=file(p3)
00786              uid1=fx.readline().strip()
00787              fx.close()
00788           except Exception as e:
00789              None
00790 
00791           if uid1!=uid:
00792              return {'cm_return':1, 'cm_error':'different alias->uid disambiguator already exists ('+p3+')'}
00793 
00794        try:
00795           f=file(p3, 'w')
00796           f.write(uid+'\n')
00797           f.close()
00798           found_alias=True
00799        except Exception as e:
00800           None
00801 
00802        # Check if uid->alias exist
00803        p2=os.path.join(p1, ini['dcfg']['falias-u'] + uid)
00804        if os.path.isfile(p2):
00805           try:
00806              fx=file(p2)
00807              alias1=fx.readline().strip()
00808              fx.close()
00809           except Exception as e:
00810              None
00811 
00812           if alias1!=alias:
00813              return {'cm_return':1, 'cm_error':'different uid->alias disambiguator already exists ('+p2+')'}
00814 
00815        try:
00816           f=file(p2, 'w')
00817           f.write(alias+'\n')
00818           f.close()
00819           found_alias=True
00820        except Exception as e:
00821           None
00822 
00823     # Create directory
00824     if not os.path.exists(p):
00825        try:
00826           os.mkdir(p)
00827        except Exception as e:
00828           return {'cm_return':1, 'cm_error':format(e)}
00829 
00830     uoa=uid
00831     if alias!='': uoa=alias
00832 
00833     return {'cm_return':0, 'cm_path':p, 'cm_uid':uid, 'cm_alias':alias, 'cm_uoa':uoa}
00834 
00835 # ============================================================================
00836 def check_global_writing(i):
00837     """
00838     Check global writing to repository
00839 
00840     Input:  {
00841             }
00842 
00843     Output: {
00844               cm_return  - return code >0 if writing is not allowed
00845             }
00846     """
00847 
00848     if cm_kernel.ini['dcfg'].get('forbid_write','')=='yes':
00849        return {'cm_return':1, 'cm_error':'any writing is forbidden by configuration'}
00850 
00851     return {'cm_return':0}
00852 
00853 # ============================================================================
00854 def delete_entry(i):
00855     """
00856     Delete entry
00857 
00858     Input:  {
00859               cm_path        - path to the entry
00860               cm_path_module - part of the path with module
00861               cm_uid         - uid (from UOA), needed to delete alias disambiguator if exists
00862               cm_alias       - alias (from UOA), needed to delete alias disambiguator if exists
00863             }
00864 
00865     Output: {
00866               cm_return  - return code >0 if error
00867             }
00868     """
00869 
00870     r=check_global_writing({})
00871     if r['cm_return']>0: return r
00872 
00873     # Remove alias if exists
00874     if i['cm_alias']!='':
00875        # Delete alias
00876        r=cm_kernel.delete_alias({'cm_path':i['cm_path_module'], 'cm_alias':i['cm_alias'], 'cm_uid':i['cm_uid']})
00877        if r['cm_return']>0: return r
00878 
00879     # Delete directory
00880     return cm_kernel.delete_directory({'cm_path':i['cm_path']})
00881 
00882 # ============================================================================
00883 def delete_alias(i):
00884     """
00885     Delete alias
00886 
00887     Input:  {
00888               cm_path    - path to the entry
00889               cm_uid     - uid (from UOA)
00890               cm_alias   - alias (from UOA)
00891             }
00892 
00893     Output: {
00894               cm_return  - return code >0 if error
00895             }
00896     """
00897 
00898     p=i['cm_path']
00899     uid=''
00900 
00901     if 'cm_alias' in i:
00902        alias=i['cm_alias']
00903        if alias!='' and os.path.isdir(p):
00904           p1=os.path.join(p, ini['dcfg']['dcm'], ini['dcfg']['falias-a'] + alias)
00905           if os.path.isfile(p1):
00906              try:
00907                 f=file(p1)
00908                 uid=f.readline().strip()
00909                 f.close()
00910              except Exception as e:
00911                 None
00912              os.remove(p1)
00913 
00914           if uid=='': uid=i['cm_uid']
00915 
00916           if uid!='':
00917              p1=os.path.join(p, ini['dcfg']['dcm'], ini['dcfg']['falias-u'] + uid)
00918              if os.path.isfile(p1):
00919                 os.remove(p1)
00920 
00921     return {'cm_return':0}
00922 
00923 # ============================================================================
00924 def substitute_str_in_file(i):
00925     """
00926     Substitute string in file
00927 
00928     Input:  {
00929               cm_file    - file
00930               cm_string1 - string to be replaced
00931               cm_string2 - replace string
00932             }
00933 
00934     Output: {
00935               cm_return  - return code >0 if error
00936             }
00937     """
00938 
00939     import codecs
00940 
00941     fil=i['cm_file']
00942     s1=i['cm_string1']
00943     s2=i['cm_string2']
00944 
00945     f=open(fil, 'r')
00946     x=f.read().decode('utf-8')
00947     f.close()
00948 
00949     x=x.replace(s1,s2)
00950    
00951     f=codecs.open(fil,'w','utf-8')
00952     f.write(x)
00953     f.close()
00954 
00955     return {'cm_return':0}
00956 
00957 # ============================================================================
00958 def create_data_entry(i):
00959     """
00960     Create data entry
00961 
00962     Input:  {
00963               cm_path                  - path to the repository
00964               cm_module_uoa            - module UOA 
00965               cm_data_uoa              - data UOA 
00966               (cm_data_uid)            - if cm_data_uoa==alias, we can force a given UID through this variable
00967               (cm_array)               - array to add to the entry
00968                                          If not set, the whole array will be added
00969                                          including all working variables - we do not suggest to do that
00970               (cm_add_default)         - if 'yes', add default params from descriptions (module/kernel)
00971               (cm_update)              - if 'yes', update entry
00972               (cm_description)         - description of the data entry
00973               (cm_display_as_alias)    - user readable alias to display instead of module:data
00974               (cm_skip_update_info)    - if == 'yes', do not write 'cm_updated'
00975               (cm_note)                - note about this data entry
00976               (cm_like)                - if 'yes' add +1 for this user to the entry
00977 
00978               (cm_file_upload_tmp_uid) - normally generated automatically by web server
00979                                          when uploading files
00980               (cm_file_upload_name)    - preffered uploaded file name 
00981               (cm_file_upload_type)    - type of file (if .zip, it will be automatically unziped)
00982               (cm_archive)             - if file should be kept as archive
00983 
00984               ...                      - if 'cm_array' not present, add everything else from this array
00985                                          to the entry 
00986               (lock_uid)               - unlock data
00987             }
00988 
00989     Output: {
00990               cm_return  - return code >0 if error
00991                                        -1 if entry exists
00992               cm_path    - path to created data entry
00993               cm_uid     - uid (from UOA)
00994               cm_alias   - alias (from UOA)
00995             }
00996     """
00997 
00998     r=check_global_writing({})
00999     if r['cm_return']>0: return r
01000 
01001     # Check if file upload
01002     upload_file_uid=i.get('cm_file_upload_tmp_uid','')
01003     upload_file_name=i.get('cm_file_upload_name','')
01004     upload_file_type=i.get('cm_file_upload_type','')
01005     upload_archive=i.get('cm_archive','')
01006     if upload_file_uid!='': 
01007        del(i['cm_file_upload_tmp_uid'])
01008        if 'upload_file_type' in i: del(i['cm_file_upload_type'])
01009        if upload_file_name!='' and i.get('cm_archive','')!='already_archived_with_user_name': del(i['cm_file_upload_name'])
01010 
01011     if upload_file_name=='' and 'cm_file_upload_name' in i: del(i['cm_file_upload_name'])
01012     if upload_file_type=='' and 'cm_file_upload_type' in i: del(i['cm_file_upload_type'])
01013 
01014     # Check user
01015     cm_user_uid=''
01016     cm_user_uoa=''
01017     if 'cm_user_uid' in ini['web_auth']: 
01018        cm_user_uid=ini['web_auth']['cm_user_uid']
01019        cm_user_uoa=ini['web_auth']['cm_user_uoa']
01020 
01021     p=i['cm_path']
01022 
01023     # Create first level entry (module)
01024     # Find path to module
01025     r=load_data({'cm_module_uoa':ini['dcfg']['cmr_module'],
01026                  'cm_data_uoa':i['cm_module_uoa']})
01027     if r['cm_return']>0: return r
01028     elif r['cm_return']<0: 
01029        return {'cm_return':16, 'cm_error':'can\'t find path to module "'+i['cm_module_uoa']+'"'}
01030     uid=r['cm_uid']
01031     alias=r['cm_alias']
01032     module_desc=r['cm_data_obj']['cfg']
01033 
01034     if alias=='': alias=uid
01035 
01036     r=create_entry({'cm_path':p, 'cm_data_uoa':alias, 'cm_data_uid':uid})
01037     if r['cm_return']>0: return r
01038     p1=r['cm_path']
01039 
01040     # Create second level entry (data)
01041     i1={}; i1['cm_path']=p1
01042     if 'cm_data_uoa' in i: i1['cm_data_uoa']=i['cm_data_uoa']
01043     if 'cm_data_uid' in i: i1['cm_data_uid']=i['cm_data_uid']
01044     r=create_entry(i1)
01045     if r['cm_return']>0: return r
01046 
01047     # Configuration
01048     p2=r['cm_path']
01049     p3=os.path.join(p2, ini['dcfg']['dcm'])
01050     p4=os.path.join(p3, ini['dcfg']['fconfig'])
01051     p5=os.path.join(p3, ini['dcfg']['flock'])
01052 
01053     a={}
01054     if r['cm_return']<0:
01055        if i.get('cm_update','')=='yes':
01056           # Entry exists, check locks
01057           if os.path.isfile(p5):
01058              try:
01059                 f=file(p5)
01060                 luid=f.readline().strip()
01061                 exp=f.readline().strip()
01062                 f.close()
01063              except Exception as e:
01064                 return {'cm_return':1, 'cm_error':'problem reading locking file'}
01065 
01066              # Check if lock expired
01067              if exp=='': exp=ini['dcfg'].get('flock_expire_max','60')
01068 
01069              dt=os.path.getmtime(p5)+float(exp)-time.time()
01070              if dt<0:
01071                 # Expired
01072                 if i.get('lock_uid','')=='':
01073                    os.remove(p5)
01074                 else:
01075                    return {'cm_return':32, 'cm_error':'data lock UID is not matching'}
01076 
01077              else:   
01078                 if i.get('lock_uid','')=='':
01079                    return {'cm_return':32, 'cm_error':'data is locked'}
01080                 elif i.get('lock_uid','')!=luid:
01081                    return {'cm_return':32, 'cm_error':'data is locked with different UID'}
01082              
01083           elif i.get('lock_uid','')!='':
01084              return {'cm_return':32, 'cm_error':'lock removed or expired'}
01085 
01086           # Entry exists, load configuration if update
01087           r['cm_return']=0
01088           r2=load_json_file({'cm_filename':p4})
01089           if r2['cm_return']>0: return r2
01090           a=copy.deepcopy(r2['cm_array'])
01091 
01092     else:
01093        # Create configuration directory
01094        if not os.path.isdir(p3):
01095           try:
01096              os.mkdir(p3)
01097           except Exception as e:
01098              return {'cm_return':1, 'cm_error':format(e)}
01099 
01100     # Check if cm_array in input - then use only this array as input
01101     cma={}
01102 
01103     # Check if need to add default data from description
01104     if i.get('cm_add_default','')=='yes':
01105        df={}
01106 
01107        if 'cm_common_data_description7' in ini['dcfg']:
01108           rz=create_default_array_from_description({'cm_data_desc':ini['dcfg']['cm_common_data_description7']})
01109           if rz['cm_return']>0: return rz
01110           merge_arrays({'cm_array':df, 'cm_array1':rz['cm_array']})
01111 
01112        if 'cm_common_data_description8' in ini['dcfg']:
01113           rz=create_default_array_from_description({'cm_data_desc':ini['dcfg']['cm_common_data_description8']})
01114           if rz['cm_return']>0: return rz
01115           merge_arrays({'cm_array':df, 'cm_array1':rz['cm_array']})
01116 
01117        if 'cm_data_description' in module_desc:
01118           rz=create_default_array_from_description({'cm_data_desc':module_desc['cm_data_description']})
01119           if rz['cm_return']>0: return rz
01120           merge_arrays({'cm_array':df, 'cm_array1':rz['cm_array']})
01121 
01122        if len(df)>0:
01123           merge_arrays({'cm_array':cma, 'cm_array1':df})
01124 
01125     if 'cm_array' in i: 
01126        merge_arrays({'cm_array':cma, 'cm_array1':i['cm_array']})
01127 
01128     # Update array
01129     merge_arrays({'cm_array':a, 'cm_array1':cma})
01130 
01131     if 'cm_description' in i: a['cm_description']=i['cm_description']
01132     if 'cm_display_as_alias' in i: a['cm_display_as_alias']=i['cm_display_as_alias']
01133 
01134     if 'powered_by' not in a:
01135        a['powered_by']={}
01136        a['powered_by']['name']='Collective Mind Engine'
01137        rq=get_cm_version({})
01138        if rq['cm_return']>0: return rq
01139        a['powered_by']['version']=rq['cm_string']
01140 
01141     # Add record time to configuration
01142     if i.get('cm_skip_update_info','')!='yes':
01143        u={}
01144        if cm_user_uid!='': 
01145           u['cm_user_uoa']=cm_user_uid
01146           u['cm_person_uoa']=cm_user_uid
01147        if 'cm_note' in i: u['cm_note']=i['cm_note']
01148        if 'cm_validated' in i: u['cm_validated']=i['cm_validated']
01149        if 'cm_like' in i and i['cm_like']=='yes': 
01150           # Check that only 1 rank
01151           rank=1
01152           if 'cm_updated' in a:
01153              found=False
01154              for x in a['cm_updated']:
01155                  if x.get('cm_like','')=='yes':
01156                     if 'cm_user_uoa' in x and (cm_user_uid!='' or cm_user_uoa!='') and \
01157                        (x['cm_user_uoa']==cm_user_uid or x['cm_user_uoa']==cm_user_uoa):
01158                        found=True
01159                     else:
01160                        rank+=1
01161              if not found:
01162                 u['cm_like']=i['cm_like']
01163 
01164           a['cm_rank']=rank
01165 
01166        r5=get_current_date_time({})
01167        u['cm_iso_datetime']=r5['cm_iso_datetime']
01168        u['cm_module_uid']=uid
01169 
01170        if 'cm_updated' not in a: 
01171           a['cm_updated']=[]
01172        a['cm_updated'].append(u)
01173 
01174     # Check if no cm_access_control - use default
01175     if 'cm_access_control' in a:
01176        pass
01177     elif 'cm_access_control' in i:
01178        a.update({'cm_access_control':i['cm_access_control']})
01179     elif 'cm_access_control_default' in cm_kernel.ini['dcfg']:
01180        a.update({'cm_access_control':cm_kernel.ini['dcfg']['cm_access_control_default']})
01181 
01182     r4=save_array_to_file_as_json({'cm_filename':p4, 'cm_array':a})
01183     if r4['cm_return']>0: return r4
01184 
01185     # Finish upload
01186     pp1=''
01187     if upload_file_uid!='':
01188        pp1=os.path.join(ini[cm_kernel.env_cm_tmp], ini['dcfg']['cm_tmp_prefix']+upload_file_uid+ini['dcfg']['cm_tmp_postfix'])
01189     else:
01190        # If upload made through direct call from other modules
01191        if ('cm_file_upload' in i and len(i['cm_file_upload'])>0) or \
01192           ('cm_file_upload_base64' in i and len(i['cm_file_upload_base64'])>0):
01193 
01194           if ('cm_file_upload' in i and len(i['cm_file_upload'])>0):
01195              xn=i['cm_file_upload']
01196           elif ('cm_file_upload_base64' in i and len(i['cm_file_upload_base64'])>0):
01197              xn=base64.urlsafe_b64decode(str(i['cm_file_upload_base64'])) # convert from unicode to str since base64 works on strings
01198 
01199           px=os.path.join(p2,i.get('cm_file_upload_name','default_name'))
01200           fx=file(px, 'wb')
01201           fx.write(xn)
01202           fx.close()
01203 
01204           pp1=px
01205     
01206     if pp1!='':
01207        if upload_file_type=='.zip' and upload_archive!='already_archived_with_user_name': 
01208           upload_file_name=''
01209 
01210        if upload_file_type=='' or upload_archive!='':
01211           # regular file
01212           if os.path.isfile(pp1):
01213              x=upload_file_name
01214              if x=='': x=ini['dcfg']['cm_default_upload_filename']
01215              if upload_archive=='already_archived_with_internal_name': x=ini['dcfg']['cm_archive_filename']
01216              pp2=os.path.join(p2, x)
01217              shutil.move(pp1,pp2)
01218        elif upload_file_type=='.zip':
01219           # Archive
01220           f=open(pp1,'rb')
01221           z=zipfile.ZipFile(f)
01222           for d in z.namelist():
01223               if not d.startswith('.') and not d.startswith('/') and not d.startswith('\\'):
01224                  if d.endswith('/'): 
01225                     # create directory 
01226                     pp2=os.path.join(p2,d)
01227                     if not os.path.exists(pp2): os.makedirs(pp2)
01228                  else:
01229                     # extract file
01230                     pp2=os.path.join(p2, d)
01231                     fo=open(pp2, 'wb')
01232                     fo.write(z.read(d))
01233                     fo.close()
01234           f.close()
01235           os.remove(pp1)   
01236 
01237     # Remove lock after update
01238     if i.get('lock_uid','')!='' and os.path.isfile(p5):
01239        os.remove(p5)
01240 
01241     return r
01242 
01243 # ============================================================================
01244 def create_default_array_from_description(i):
01245     """
01246     Create array with default parameters from data description
01247 
01248     Input:  {
01249               cm_data_desc    - cM data description
01250             }
01251 
01252     Output: {
01253               cm_return       - return code =0 if successful
01254                                             >0 if error
01255               cm_array        - array with default parameters
01256             }
01257     """
01258 
01259     a={}
01260 
01261     for x in i['cm_data_desc']:
01262         y=i['cm_data_desc'][x]
01263 
01264         if '$' not in x:
01265            d=''
01266 
01267            t=y.get('type','')
01268            if t=='list': d=[]
01269            elif t=='dict': d={}
01270 
01271            if 'default_value' in d: d=y['default_value']
01272 
01273            set_value_by_flattened_key({'cm_array':a, 'cm_key':x, 'cm_value':d})
01274 
01275     return {'cm_return':0, 'cm_array':a}
01276 
01277 # ============================================================================
01278 def convert_file_to_upload_string(i):
01279     """
01280     Convert file to upload string
01281 
01282     Input:  {
01283               cm_full_filename - file name to convert
01284             }
01285 
01286     Output: {
01287               cm_return            - return code =0 if successful
01288                                                  >0 if error
01289               cm_file_upload_base64 - string that can be used to upload file
01290             }
01291     """
01292 
01293     fn=i['cm_full_filename']
01294 
01295     if not os.path.isfile(fn):
01296        return {'cm_return':1, 'cm_error':'file '+fn+' not found'}
01297 
01298     s=''
01299     try:
01300        f=open(fn, 'rb')
01301        while True:
01302           x = f.read(32768);
01303           if not x: break
01304           s+=x
01305        f.close()
01306     except Exception as e:
01307        return {'cm_return':1, 'cm_error':'error reading file ('+format(e)+')'}
01308 
01309     r={'cm_return':0}
01310     r['cm_file_upload_base64']=base64.urlsafe_b64encode(s)
01311 
01312     return r
01313 
01314 # ============================================================================
01315 def get_current_date_time(i):
01316     """
01317     Get current date and time
01318 
01319     Input:  {}
01320 
01321     Output: {
01322               cm_return       - return code =0 if successful
01323                                             >0 if error
01324               cm_array        - array with date and time
01325               cm_iso_datetime - date and time in ISO format
01326             }
01327     """
01328 
01329     a={}
01330 
01331     now1=datetime.datetime.now()
01332     now=now1.timetuple()
01333 
01334     a["cm_date_year"]=str(now[0])
01335     a["cm_date_month"]=str(now[1])
01336     a["cm_date_day"]=str(now[2])
01337     a["cm_time_hour"]=str(now[3])
01338     a["cm_time_minute"]=str(now[4])
01339     a["cm_time_second"]=str(now[5])
01340 
01341 #    return {'cm_return':0, 'cm_array':a, 'cm_iso_datetime':now1.isoformat(' ')}
01342     return {'cm_return':0, 'cm_array':a, 'cm_iso_datetime':now1.isoformat()}
01343 
01344 # ============================================================================
01345 def convert_iso_time_to_str(i):
01346     """
01347     Get current date and time
01348 
01349     Input:  {
01350               cm_iso_datetime    - date and time in ISO format
01351               (cm_custom_format) - customize view
01352             }
01353 
01354     Output: {
01355               cm_return       - return code =0 if successful
01356                                             >0 if error
01357               cm_string       - user friendly date/time
01358             }
01359     """
01360 
01361     if 'cm_iso_datetime' not in i: return {'cm_return':1, 'cm_error':'"cm_iso_datetime" is not set in kernel/convert_iso_time_to_str'}
01362 
01363     d=datetime.datetime(*map(int, re.split('[^\d]', i['cm_iso_datetime'])[:-1]))
01364 
01365     format=i.get('cm_custom_format','')
01366     if format=='':
01367        format='%Y-%B-%d (%A) %H:%M:%S'
01368     s=d.strftime(format)
01369 
01370     return {'cm_return':0, 'cm_string':s}
01371 
01372 # ============================================================================
01373 def find_index_in_array(i):
01374     """
01375     Find index in array of formart [{cm_index:'a', ...}, {cm_index='b', ...}, ...]
01376     We use such array instead of simple associative array 
01377     when we need to keep an order such as for actions ... 
01378 
01379     Input:  {
01380               cm_array - array of format
01381               cm_index - index
01382             }
01383 
01384     Output: {
01385               cm_return - return code =  0 if successful
01386                                         -1 if not found
01387               cm_array  - sub-array with given index
01388             }
01389     """
01390 
01391     for x in i['cm_array']:
01392         if 'cm_index' in x and x['cm_index']==i['cm_index']:
01393            return {'cm_return':0, 'cm_array':x}
01394 
01395     return {'cm_return':-1}
01396 
01397 
01398 # ============================================================================
01399 def find_path_to_current_repository(i):
01400     """
01401     Finds local repository (recursively checks 
01402     all upper directories until it finds the repository)
01403 
01404     Input:  {
01405               cm_get_uid      - if 'yes' load data to get UID besides UOA
01406             }
01407 
01408     Output: {
01409               cm_return       - return code >0 if error
01410                                             -1 if doesn't exist
01411               cm_path         - path to the local repository
01412               (cm_module_uoa) - module UOA of the current path
01413               (cm_module_uid) - module UID of the current path
01414               (cm_data_uoa)   - data UOA of the current path
01415               (cm_data_uid)   - data UID of the current path
01416             }
01417     """
01418 
01419     pe=ini['dcfg']['cmr_ext']
01420 
01421     px=os.path.join(os.getcwd())
01422     py="dummy"
01423 
01424     paths=[]
01425 
01426     p=os.path.join(px,pe)
01427     if os.path.isdir(p)==True:
01428        return {'cm_return':0, 'cm_path':p}
01429 
01430     while py!="":
01431        px0=os.path.split(px)
01432        px=px0[0]
01433        px1=px0[1]
01434        py=os.path.split(px)[1]
01435        p=os.path.join(px,pe)
01436        if os.path.isdir(p):
01437           r={'cm_return':0, 'cm_path':p}
01438           if len(paths)==1:
01439              r['cm_module_uoa']=paths[0]
01440           elif len(paths)>=2:
01441              l=len(paths)
01442              r['cm_module_uoa']=paths[l-1]
01443              r['cm_data_uoa']=paths[l-2]
01444 
01445           if i.get('cm_get_uid','')=='yes' and r.get('cm_data_uoa','')!='':
01446              r1=copy.deepcopy(r)
01447              rx=load_data(r1)
01448              if rx['cm_return']>0: return rx
01449 
01450              r['cm_module_uid']=rx['cm_module_uid']
01451              r['cm_data_uid']=rx['cm_uid']
01452           return r
01453        else:
01454           paths.append(px1)
01455 
01456     return {'cm_return':-1}
01457 
01458 # ============================================================================
01459 def find_path_to_work_repositories(i):
01460     """
01461     Find path to work and local repositories
01462 
01463     Input:  {
01464             }
01465 
01466     Output: {
01467               cm_return  - return code >0 if error
01468               cm_array   - array of paths
01469             }
01470     """
01471 
01472     a=[]
01473 
01474     r=find_path_to_current_repository({})
01475     if r['cm_return']>0: return r
01476     elif r['cm_return']==0:
01477        a.append(r['cm_path'])
01478 
01479     # Check local repository
01480     if 'cm_local_repo' in cm_kernel.ini:
01481        p=cm_kernel.ini['cm_local_repo']
01482        found=False
01483        for q in a:
01484            if os.stat(q)==os.stat(p):
01485               found=True
01486               break
01487        if not found: a.append(p)
01488 
01489     # Check default repository
01490     p=cm_kernel.ini['cm_default_repo']
01491     found=False
01492     for q in a:
01493         if os.stat(q)==os.stat(p):
01494            found=True
01495            break
01496     if not found: a.append(p)
01497 
01498     return {'cm_return':0, 'cm_array':a}
01499 
01500 # ============================================================================
01501 def load_json_file(i):
01502     """
01503     Load json from file
01504 
01505     Input:  {
01506               cm_filename - file name with json
01507             }
01508 
01509     Output: {
01510               cm_return - return code = 0 if successful
01511               cm_array  - array from json file
01512             }
01513     """
01514 
01515     if 'cm_filename' not in i:
01516        return {'cm_return':1, 'cm_error':'parameter "cm_filename" is not defined'}
01517 
01518     try:
01519        f=file(i['cm_filename'])
01520     except Exception as e:
01521        return {'cm_return':1, 'cm_error':format(e)}
01522 
01523     try:
01524        array=json.loads(f.read())
01525     except Exception as e:
01526        f.close()
01527        return {'cm_return':1, 'cm_error':'problem loading array from file='+i['cm_filename']+' ('+format(e)+')'}
01528 
01529     f.close()
01530 
01531     return {'cm_return':0, 'cm_array': array}
01532 
01533 # ============================================================================
01534 def save_array_to_file_as_json(i):
01535     """
01536     Save array to file
01537 
01538     Input:  {
01539               cm_filename - file name with json
01540               cm_array    - array to save
01541             }
01542 
01543     Output: {
01544               cm_return - return code = 0 if successful
01545             }
01546     """
01547 
01548     if 'cm_filename' not in i:
01549        return {'cm_return':1, 'cm_error':'parameter "cm_filename" is not defined'}
01550 
01551     if 'cm_array' not in i:
01552        return {'cm_return':1, 'cm_error':'parameter "cm_array" is not defined'}
01553 
01554     s = json.dumps(i['cm_array'], indent=2, sort_keys=True, encoding="utf-8")
01555 
01556     try:
01557        f=file(i['cm_filename'], 'w')
01558     except Exception as e:
01559        return {'cm_return':1, 'cm_error':format(e)}
01560 
01561     try:
01562        f.write(s+'\n')
01563     except Exception as e:
01564        f.close()
01565        return {'cm_return':1, 'cm_error':'problem writing array to file='+i['cm_filename']+' ('+format(e)+')'}
01566 
01567     f.close()
01568 
01569     return {'cm_return':0}
01570 
01571 # ============================================================================
01572 def load_array_from_file(i):
01573     """
01574     Load array from file
01575 
01576     Input:  {
01577               cm_filename - file name
01578             }
01579 
01580     Output: {
01581               cm_return  - return code = 0 if successful
01582               cm_array   - array
01583             }
01584     """
01585 
01586     if 'cm_filename' not in i:
01587        return {'cm_return':1, 'cm_error':'parameter "cm_filename" is not defined'}
01588 
01589     array=[]
01590 
01591     try:
01592        f=file(i['cm_filename'])
01593     except Exception as e:
01594        return {'cm_return':1, 'cm_error':format(e)}
01595 
01596     try:
01597        for line in f:
01598            array.append(line)
01599     except Exception as e:
01600        f.close()
01601        return {'cm_return':1, 'cm_error':'problem loading array from file='+i['cm_filename']+' ('+format(e)+')'}
01602 
01603     f.close()
01604 
01605     return {'cm_return':0, 'cm_array': array}
01606 
01607 # ============================================================================
01608 def save_flat_array_to_file(i):
01609     """
01610     Load array from file
01611 
01612     Input:  {
01613               cm_array       - array to save (only flat one)
01614               cm_filename    - file name
01615               separate_lines - if 'yes', put key and value on separate lines
01616             }
01617 
01618     Output: {
01619               cm_return  - return code = 0 if successful
01620             }
01621     """
01622 
01623     if 'cm_filename' not in i: return {'cm_return':1, 'cm_error':'parameter "cm_filename" is not defined'}
01624     if 'cm_array' not in i: return {'cm_return':1, 'cm_error':'parameter "cm_array" is not defined'}
01625 
01626     separate=False
01627     if i.get('separate_lines','')=='yes': separate=True
01628 
01629     a=i['cm_array']
01630 
01631     try:
01632        f=file(i['cm_filename'], 'w')
01633        for x in a:
01634            if separate:
01635               f.write(str(x)+'\n')
01636               f.write(str(a[x])+'\n')
01637            else:
01638               f.write(str(x)+'='+str(a[x])+'\n')
01639        f.close()
01640     except Exception as e:
01641        return {'cm_return':1, 'cm_error':format(e)}
01642 
01643     return {'cm_return':0}
01644 
01645 # ============================================================================
01646 def flatten_array(i):
01647     """
01648     Flatten array
01649     Any list item is converted to @number=value
01650     Any dict item is converted to #key=value
01651     # is always added at the beginning 
01652 
01653     Input:  {
01654               cm_array        - full array
01655               prefix          - prefix (for recursion)
01656             }
01657 
01658     Output: {
01659               cm_return - if =0, success
01660               cm_array  - flattened array
01661             }
01662     """
01663 
01664     # Check vars
01665     if 'cm_array' not in i: return {'cm_return':1, 'cm_error':'parameter "cm_array" is not defined in "kernel flatten_array"'}
01666 
01667     prefix='#'
01668     if 'prefix' in i: prefix=i['prefix']
01669 
01670     a=i['cm_array']
01671     aa={}
01672 
01673     flatten_array_internal(a, aa, prefix)
01674 
01675     return {'cm_return':0, 'cm_array': aa}
01676 
01677 # ============================================================================
01678 def flatten_array_internal(a, aa, prefix):
01679     # Start flattening
01680     if type(a) is dict or type(a) is list:
01681        i=0
01682        for x in a:
01683            if type(a) is dict: 
01684               v=a[x] 
01685               prefix1=prefix+'#'+x
01686            else: 
01687               prefix1=prefix+'@'+str(i)
01688               v=x
01689            if type(v) is dict or type(v) is list:
01690               flatten_array_internal(v, aa, prefix1)
01691            else:
01692               aa[prefix1]=v
01693            i+=1
01694     else:
01695        aa[prefix]=a
01696 
01697     return {'cm_return':0, 'cm_array': a}
01698 
01699 # ============================================================================
01700 def get_value_by_flattened_key(i):
01701     """
01702     Get value from array using flattened key
01703 
01704     Input:  {
01705               cm_array - array
01706               cm_key   - flattened key
01707             }
01708 
01709     Output: {
01710               cm_return - if =0, success
01711               cm_value  - value or None
01712             }
01713     """
01714     # Check vars
01715     if 'cm_array' not in i: return {'cm_return':1, 'cm_error':'"cm_array" is not defined in "kernel get_value_by_flattened_key"'}
01716     if 'cm_key' not in i: return {'cm_return':1, 'cm_error':'"cm_key" is not defined in "kernel get_value_by_flattened_key"'}
01717 
01718     v=None
01719 
01720     a=i['cm_array']
01721     k=i['cm_key']
01722  
01723     # Remove leading # if there 
01724     if len(k)>0 and k[0:1]=='#': k=k[1:]
01725 
01726     k1=''
01727     kt='' # type '#' or '@'
01728     x=0
01729     finish=False
01730 
01731     while not finish:
01732         y=k[x]
01733         x+=1
01734 
01735         if y=='#' or y=='@':
01736            if kt=='#':
01737               if k1 not in a: break
01738               a=a[k1]
01739            elif kt=='@':
01740               if len(a)<=long(k1): break
01741               a=a[long(k1)]
01742            k1=''
01743            kt=y
01744         else:
01745            k1+=y
01746 
01747         if x>=len(k): break
01748 
01749     if k1!='' and kt!='':
01750        if kt=='#':   
01751           if k1 in a: v=a[k1]
01752        else:         
01753           if len(a)>long(k1): v=a[long(k1)]
01754 
01755     return {'cm_return':0, 'cm_value': v}
01756 
01757 # ============================================================================
01758 def set_value_by_flattened_key(i):
01759     """
01760     Set value in array using flattened key
01761 
01762     Input:  {
01763               cm_array           - array (it will be directly changed!)
01764               cm_key             - flattened key (or not if doesn't start with #)
01765               cm_value           - value to set
01766               cm_replace_in_keys - array with strings to replace in keys
01767                                    (used in forms to process # and @ through ^35^ and ^64^ for example)
01768             }
01769 
01770     Output: {
01771               cm_return - if =0, success
01772               cm_array  - modified array
01773             }
01774     """
01775     # Check vars
01776     if 'cm_array' not in i: return {'cm_return':1, 'cm_error':'"cm_array" is not defined in "kernel get_value_by_flattened_key"'}
01777     if 'cm_key' not in i: return {'cm_return':1, 'cm_error':'"cm_key" is not defined in "kernel get_value_by_flattened_key"'}
01778     if 'cm_value' not in i: return {'cm_return':1, 'cm_error':'"cm_value" is not defined in "kernel get_value_by_flattened_key"'}
01779 
01780     rep={}
01781     if 'cm_replace_in_keys' in i: rep=i['cm_replace_in_keys']
01782 
01783     a=i['cm_array']
01784     k=i['cm_key']
01785     v=i['cm_value']
01786 
01787     if len(k)>0 and k[0]!='#':
01788        k=replace_from_dict(k, rep)
01789        a[k]=v
01790     else:
01791        # Remove leading # if there 
01792        if len(k)>0 and k[0:1]=='#': k=k[1:]
01793 
01794        k1=''
01795        kt='' # type '#' or '@'
01796        x=0
01797        finish=False
01798 
01799        while not finish:
01800            y=k[x]
01801            x+=1
01802 
01803            if y=='#' or y=='@':
01804               if kt=='#':
01805                  k1=replace_from_dict(k1, rep)
01806                  if k1 not in a: 
01807                     if y=='#': a[k1]={}
01808                     else: a[k1]=[]
01809                  a=a[k1]
01810               elif kt=='@':
01811                  if len(a)<=long(k1): 
01812                     for q in range(len(a)-1,long(k1)):
01813                         if y=='#': a.append({})
01814                         else: a.append([])
01815                  a=a[long(k1)]
01816               k1=''
01817               kt=y
01818            else:
01819               k1+=y
01820 
01821            if x>=len(k): break
01822 
01823        if k1!='' and kt!='':
01824           if kt=='#':   
01825              k1=replace_from_dict(k1, rep)
01826              a[k1]=v
01827           else:         
01828              if len(a)<=long(k1): 
01829                 for q in range(len(a)-1,long(k1)):
01830                     if y=='#': a.append({})
01831                     else: a.append([])
01832              a[long(k1)]=v
01833 
01834     return {'cm_return':0, 'cm_array': i['cm_array']}
01835 
01836 # ============================================================================
01837 def replace_from_dict(s, rep):
01838     """
01839     Replace substrings from array rep in string s
01840 
01841     Input:  s   - string
01842             rep - array with substrings to replace
01843 
01844     Output: new string
01845     """
01846 
01847     for x in rep:
01848         s=s.replace(x, rep[x])
01849 
01850     return s
01851 
01852 # ============================================================================
01853 def restore_flattened_array(i):
01854     """
01855     Restore flattened array
01856 
01857     Input:  {
01858               cm_array             - flattened array
01859               (cm_replace_in_keys) - array with strings to replace in keys
01860                                      (used in forms to process # and @ through ^35^ and ^64^ for example)
01861             }
01862 
01863     Output: {
01864               cm_return - if =0, success
01865               cm_array  - restored array
01866             }
01867     """
01868     # Check vars
01869     if 'cm_array' not in i: return {'cm_return':1, 'cm_error':'"cm_array" is not defined in "kernel restore_flattened_key"'}
01870 
01871     rep={}
01872     if 'cm_replace_in_keys' in i: rep=i['cm_replace_in_keys']
01873 
01874     a={} # default
01875     b=i['cm_array']
01876     first=True
01877     for x in b:
01878         if first: 
01879            first=False
01880            y=x[1:2]
01881            if y=='@': a=[]
01882            else: a={}
01883 
01884         cm_kernel.set_value_by_flattened_key({'cm_array':a, 'cm_key':x, 'cm_value':b[x], 'cm_replace_in_keys':rep})
01885 
01886     return {'cm_return':0, 'cm_array': a}
01887     
01888 # ============================================================================
01889 def delete_directory(i):
01890     """
01891     Delete a given directory with subdirectories (be careful)
01892 
01893     Input:  {
01894               cm_path - path to delete
01895             }
01896 
01897     Output: {
01898               cm_return  - return code = 0 if successful
01899             }
01900     """
01901 
01902     r=check_global_writing({})
01903     if r['cm_return']>0: return r
01904 
01905     if 'cm_path' not in i:
01906        return {'cm_return':1, 'cm_error':'parameter "cm_path" is not defined'}
01907     p=i['cm_path']
01908 
01909     if not os.path.isdir(p):
01910        return {'cm_return':1, 'cm_error':p+' is not directory'}
01911 
01912     forbid=False
01913     if os.path.isdir(ini['cm_default_repo']) and os.stat(p)==os.stat(ini['cm_default_repo']): forbid=True
01914     if os.path.isdir(ini[env_cm_root]) and os.stat(p)==os.stat(ini[env_cm_root]): forbid=True
01915 
01916     if 'forbid_deletion_of_dirs' in ini['dcfg']:
01917        for x in ini['dcfg']['forbid_deletion_of_dirs']:
01918            if os.path.isdir(x) and os.stat(p)==os.stat(x): forbid=True; break
01919 
01920     if forbid:
01921        return {'cm_return':1, 'cm_error': 'forbidden to delete '+p}
01922 
01923     shutil.rmtree(p)
01924 
01925     return {'cm_return':0}
01926 
01927 # ============================================================================
01928 def convert_string_to_html(i):
01929     """
01930     Convert string to html (remove special characters)
01931 
01932     Input:  {
01933               cm_string  - string to process
01934               cm_skip_br - if 'yes', do not add <BR>
01935             }
01936 
01937     Output: {
01938               cm_return - return code = 0 if successful
01939               cm_string - processed string
01940             }
01941     """
01942 
01943     if 'cm_string' not in i: return {'cm_return':1, 'cm_error':'parameter "cm_string" is not defined in kernel/convert_string_to_html'}
01944 
01945     s=i['cm_string']
01946     s=s.replace('&', '&amp;')
01947     s=s.replace('<', '&lt;')
01948     s=s.replace('>', '&gt;')
01949     s=s.replace('"', '&quot;')
01950     s=s.replace('\r', '')
01951 #    s=s.replace(' ', '&nbsp;')
01952 
01953     if i.get('cm_skip_br', '')!='yes': 
01954        s=s.replace('\n', '<BR>')
01955 
01956     return {'cm_return':0, 'cm_string':s}
01957 
01958 # ============================================================================
01959 def flatten_classification(i):
01960     """
01961     Flatten classification ["class", "subclass", ..."] to class.subclass. ...
01962 
01963     Input:  {
01964               cm_classification=[]
01965             }
01966 
01967     Output: {
01968               cm_return - return code = 0 if successful
01969               cm_string - classification as string
01970             }
01971     """
01972 
01973     if 'cm_classification' not in i: return {'cm_return':1, 'cm_error':'parameter "cm_classification" is not defined in kernel/flatten_classification'}
01974 
01975     cc=i['cm_classification']
01976     cs=''
01977 
01978     first=True; y=''
01979     for x in cc:
01980         if first: first=False
01981         else: y+='.'
01982         y+=x
01983 
01984     return {'cm_return':0, 'cm_string':y}
01985 
01986 # ============================================================================
01987 def set_print_mode(i):
01988     """
01989     Set print mode
01990 
01991     Input:  {
01992               'mode' = 'on' | 'off'
01993             }
01994 
01995     Output: 
01996     """
01997     global print_on
01998 
01999     mode=i.get('mode','on')
02000     if mode=='on': print_on=True
02001     else: print_on=False
02002 
02003     return {'cm_return':0}
02004 
02005 # ============================================================================
02006 def print_for_web(s):
02007     """
02008     Print string in UTF-8
02009 
02010     Input:  s   - string
02011 
02012     Output: 
02013     """
02014 
02015     if print_on:
02016        print(s.encode("utf-8", 'ignore'))
02017 
02018     return
02019 
02020 # ============================================================================
02021 def print_for_con(s):
02022     """
02023     Print string
02024 
02025     Input:  s   - string
02026 
02027     Output: 
02028     """
02029 
02030     if print_on:
02031        print(s)
02032 
02033     return
02034 
02035 # ============================================================================
02036 def get_all_uoa(i):
02037     """
02038     Get a list of all entries in the repository
02039 
02040     Input:  {
02041               cm_path                      - path to repository
02042               (cm_module_uoa)              - module UOA. If =='', return available modules in the repository
02043                                                          (or use cm_only_available_modules explicitly)
02044               cm_only_uid                  - 'yes' to show only UIDs
02045               (cm_classes_uoa)             - prune by classes UOA
02046               (prune_data_uoa)             - prune by data UOA
02047               (cm_only_available_modules)  - if 'yes', return only available modules in a given repository
02048               (skip_data_cfg)              - if 'yes', do not add data cfg
02049               (cm_admin)                   - if 'yes', override access control - only internal use,
02050                                              can not be used during web access (explicitly removed)
02051             }
02052 
02053     Output: {
02054               cm_return     - return code = 0 if successful
02055               cm_array      - list of UOA or only UID
02056               cm_mixed      - list of {cm_uid, cm_alias, cm_uoa, cm_display_as_alias, cm_display_html, cm_data_obj_cfg}
02057             }
02058     """
02059 
02060     if 'cm_path' not in i:
02061        return {'cm_return':1, 'cm_error':'parameter "cm_path" is not defined'}
02062 
02063     sdc=False
02064     if i.get('skip_data_cfg','')=='yes': sdc=True
02065 
02066     array=[]
02067     mixed=[]
02068 
02069     pp=i['cm_path']
02070 
02071     module_uoa=i.get('cm_module_uoa','')
02072     if i.get('cm_only_available_modules','')!='yes' and module_uoa!='':
02073        r=find_path_to_entry({'cm_path':pp, 'cm_data_uoa':module_uoa})
02074        if r['cm_return']>0: return r
02075        elif r['cm_return']<0:
02076           # entries do not exist in this repository, quit
02077           return {'cm_return':0, 'cm_array':array, 'cm_mixed':mixed}
02078 
02079        p=r['cm_path']
02080     else:
02081        module_uoa=ini['dcfg']['cm_module_module_uoa']
02082        p=pp
02083        pp='' # To search in all repos
02084 
02085     only_uid=False
02086     if i.get('cm_only_uid','')=='yes': only_uid=True
02087 
02088     if os.path.isdir(p):
02089        dirList=os.listdir(p)
02090        for fn in dirList:
02091            p1=os.path.join(p,fn)
02092            if fn!=ini['dcfg']['dcm'] and fn not in ini['dcfg']['ignore_entries'] and os.path.isdir(p1):
02093               # Attempt to load
02094               ii={'cm_path':pp,
02095                   'cm_module_uoa':module_uoa,
02096                   'cm_data_uoa':fn}
02097               if i.get('cm_admin','')!='': ii['cm_admin']=i['cm_admin']
02098               r1=load_data(ii)
02099               if r1['cm_return']==0:
02100                  d=r1['cm_data_obj']['cfg']
02101 
02102                  if i.get('prune_data_uoa','')!='': 
02103                     if (r1['cm_uid']!=i['prune_data_uoa'] and r1['cm_uoa']!=i['prune_data_uoa']):
02104                        break
02105 
02106                  if 'cm_classes_uoa' in i:
02107                     add=True
02108                     if 'cm_classes_uoa' not in d: 
02109                        add=False
02110                     else:
02111                        for q in i['cm_classes_uoa']:
02112                            if q not in d['cm_classes_uoa']:
02113                               add=False
02114                               break
02115                     if not add: continue
02116                  
02117                  if only_uid:
02118                     x=r1['cm_uid']
02119                  else:
02120                     x=fn
02121 
02122                  array.append(x)
02123 
02124                  jj={'cm_uid':r1['cm_uid'], \
02125                      'cm_alias':r1['cm_alias'], \
02126                      'cm_uoa':r1['cm_uoa'], \
02127                      'cm_module_uid':r1['cm_module_uid'], \
02128                      'cm_module_uoa':r1['cm_module_uoa'], \
02129                      'cm_display_as_alias':r1['cm_display_as_alias'], \
02130                      'cm_display_html':r1['cm_display_html'], \
02131                     }
02132                  if not sdc: jj['cm_data_obj_cfg']=d
02133                  mixed.append(jj)
02134 
02135     return {'cm_return':0, 'cm_array':array, 'cm_mixed':mixed}
02136 
02137 # ============================================================================
02138 def get_list_of_all_files(i):
02139     """
02140     Get a list of all files in a given directory recursively
02141 
02142     Input:  {
02143               cm_path      - path
02144               cm_path_ext  - path extension
02145               limit        - limit number of files (if huge directories)
02146               number       - cur. number of files
02147               cm_all_files - if 'yes' do not ignore special files (like .cm)
02148             }
02149 
02150     Output: {
02151               cm_return - return code = 0 if successful
02152               cm_array  - list of files
02153             }
02154     """
02155 
02156     number=0
02157     if 'number' in i: number=i['number']
02158 
02159     limit=-1
02160     if 'limit' in i: limit=i['limit']
02161 
02162     a=[] 
02163 
02164     pe=''
02165     if 'cm_path_ext' in i: pe=i['cm_path_ext']
02166 
02167     try:
02168        dirList=os.listdir(i['cm_path'])
02169     except Exception as e:
02170         None
02171     else:
02172         for fn in dirList:
02173             # FGG: should move to cfg ...
02174             if fn!='.svn':
02175                p=os.path.join(i['cm_path'], fn)
02176                if i.get('cm_all_files','')=='yes' or (fn!=ini['dcfg']['dcm'] and fn not in ini['dcfg']['ignore_entries']):
02177                   if os.path.isdir(p):
02178                      r=get_list_of_all_files({'cm_path':os.path.join(p), 'cm_path_ext':os.path.join(pe, fn), 'number':number})
02179                      if r['cm_return']>0: return r
02180                      a.extend(r['cm_array'])
02181                   else:
02182                      a.append(os.path.join(pe, fn))
02183 
02184                   number=len(a)
02185                   if limit!=-1 and number>limit: break
02186  
02187     return {'cm_return':0, 'cm_array':a, 'number':number}
02188 
02189 # ============================================================================
02190 def get_data_description(i):
02191 
02192     """
02193     Get data entry description
02194 
02195     Input:  {
02196               cm_run_module_uoa | cm_module_uoa - module UOA
02197               (cm_desc_key)                     - if this key is specified, take description from this key
02198               (cm_flat_desc_key)                - if this flat key is specified, take description from this flat key
02199               (cm_action_key)                   - action key
02200               cm_which_action                   - action
02201               (cm_key)                          - if action is specified, use this key instead of default one
02202             }
02203 
02204     Output: {
02205               cm_return         - return code >0 if error
02206               cm_data_desc      - data description
02207               cm_params_default - data description
02208               ...               - other parameters from load_data
02209             }
02210     """
02211 
02212     # Module command params description
02213     a={}
02214     
02215     # Module default params for this action
02216     b={}
02217 
02218     # Load module
02219     r1={}
02220     if 'cm_repo_uoa' in i: r1['cm_repo_uoa']=i['cm_repo_uoa']
02221     r1['cm_data_uoa']=i['cm_module_uoa']
02222     r1['cm_module_uoa']=cm_kernel.ini['dcfg']['cmr_module']
02223     r=load_data(r1)
02224     if r['cm_return']>0: return r
02225     d=r['cm_data_obj']
02226 
02227     if i.get('cm_desc_key','')!='':
02228        a.update(d['cfg'].get(i['cm_desc_key'],{}))
02229     elif i.get('cm_flat_desc_key','')!='':
02230        rx=cm_kernel.get_value_by_flattened_key({'cm_array':d['cfg'], 'cm_key':i['cm_flat_desc_key']})
02231        if rx['cm_return']==0 and rx['cm_value']!=None: 
02232           a.update(rx['cm_value'])
02233     else:
02234        ak=i.get('cm_action_key','')
02235        if ak=='': ak='cm_actions'
02236 
02237        if 'cm_which_action' in i:
02238           if ak in d['cfg']: 
02239              r1=cm_kernel.find_index_in_array({
02240                                                'cm_array':d['cfg'][ak],
02241                                                'cm_index':i['cm_which_action']
02242                                               })
02243              if r1['cm_return']==0:
02244                 key=i.get('cm_key','')
02245                 if key=='' and 'params_desc' in r1['cm_array']: a.update(r1['cm_array']["params_desc"])
02246                 elif key!='' and key in r1['cm_array']: a.update(r1['cm_array'][key])
02247                 if 'params_default' in r1['cm_array']: b.update(r1['cm_array']["params_default"])
02248        else:
02249           # Check standard cm_data_description and redirect
02250 
02251           red=d['cfg'].get('cm_data_description_from_other_module',{})
02252           if red.get('cm_module_uoa','')!='':
02253              # Load another module
02254              ii={'cm_run_module_uoa':cm_kernel.ini['dcfg']['cmr_module'],
02255                  'cm_action':'load',
02256                  'cm_data_uoa':red['cm_module_uoa']}
02257              r=cm_kernel.access(ii)
02258              if r['cm_return']>0: return r
02259              d=r['cm_data_obj']
02260              
02261           if 'cm_data_description' in d['cfg']:
02262              a.update(d['cfg']['cm_data_description'])
02263 
02264     ii={}; ii.update(r)
02265     ii['cm_return']=0
02266     ii['cm_data_desc']=a
02267     ii['cm_params_default']=b
02268     return ii
02269 
02270 # ============================================================================
02271 def system(i):
02272 
02273     """
02274     System call with timeout (careful, may cause overheads)
02275 
02276     Input:  {
02277               cmd       - command line
02278               (timeout) - timeout in seconds (granularity 0.01 sec) - may cause overheads ...
02279             }
02280 
02281     Output: {
02282               cm_return      - return code =0 if success
02283                                            =1 if timedout and killed
02284               (cm_error)     - error text  if return code > 0
02285               cm_return_code - return code of a program
02286             }
02287     """
02288 
02289     rc=0
02290     if 'timeout' not in i or i['timeout']=='' or float(i['timeout'])==0:
02291        rc=os.system(i['cmd'])
02292     else:
02293        tstart=time.time()
02294        t=0
02295        tx=float(i['timeout'])
02296 
02297        p=subprocess.Popen(i['cmd'], shell=True)
02298 
02299        while p.poll() == None and t<tx:
02300           time.sleep(0.01)
02301           t=time.time()-tstart
02302 
02303        if t>=tx and p.poll()==None:
02304           p.kill()
02305 
02306           return {'cm_return':1, 'cm_error':'process timed out and was killed'}
02307 
02308        rc=p.returncode
02309 
02310     return {'cm_return':0, 'cm_return_code':rc}
02311 
02312 # ============================================================================
02313 def gen_cm_tmp_file(i):
02314 
02315     """
02316     Generate tmp file name
02317 
02318     Input:  {}
02319 
02320     Output: {
02321               cm_return  - return code >0 if error
02322               cm_path    - path to file
02323               cm_path1   - path to file without extension
02324                            (for example, a script will create many sub-files with different extensions)
02325               cm_uid     - generated uid
02326             }
02327     """
02328 
02329     r=gen_uid({})
02330     if r['cm_return']>0: return r
02331     uid=r['cm_uid']
02332 
02333     p1=os.path.join(ini[env_cm_tmp], ini['dcfg']['cm_tmp_prefix']+uid)
02334     p=p1+ini['dcfg']['cm_tmp_postfix']
02335 
02336     return {'cm_return':0, 'cm_path':p, 'cm_uid':uid, 'cm_path1':p1}
02337 
02338 # ============================================================================
02339 def array2cm(a):
02340     """
02341     Convert list to cm array
02342 
02343     cm_module_uoa cm_action A=B C=D ... @file1.json E=F ... -- <unparsed arguments>
02344 
02345     if extension of @file is .tmp, it will be deleted after read!
02346 
02347     Input:  input array
02348 
02349     Output: {
02350               cm_return  - return code = 0 if successful
02351               cm_array   - array
02352               cm_console - if 'cm_console' in array, set it to the value of 'cm_console' parameter
02353             }
02354     """
02355     obj={}
02356     con=''
02357 
02358     la=len(a)
02359 
02360     obj['cm_run_module_uoa']=cm_module_default
02361     obj['cm_action']=cm_action_default
02362 
02363     if la>0: obj['cm_run_module_uoa']=a[0]
02364 
02365     # Parsing
02366     for x in range(1, len(a)):
02367         p=a[x].rstrip()
02368         if p==cm_unparsed_sep:
02369 
02370            obj['cm_unparsed']=a[x+1:]
02371            break
02372 
02373         elif p.startswith("@@"):
02374            jd=p[2:]
02375            if len(jd)<2:
02376               return {'cm_return':1, 'cm_error':'can\'t parse command line option '+p, 'cm_console':con}
02377            je=check_cm_json({'cm_json':jd})
02378            obj.update(je)
02379 
02380         elif p.startswith("@"):
02381            name=p[1:]
02382            if len(name)<2:
02383               return {'cm_return':1, 'cm_error':'can\'t parse command line option '+p, 'cm_console':con}
02384 
02385            y=load_json_file({'cm_filename':name})
02386            if y['cm_return']>0: 
02387 
02388               ii={}; ii.update(y); ii.update({'cm_console':con})
02389               return ii
02390 
02391            if name.endswith('.tmp'):
02392               os.remove(name)   
02393 
02394            merge_arrays({'cm_array':obj, 'cm_array1':y['cm_array']})
02395         else:
02396            if x==1:
02397               obj['cm_action']=p
02398            else:
02399               p1=p 
02400               p2=''
02401               q=p.find("=")
02402               if q>0:
02403                  p1=p[0:q]
02404                  if len(p)>q:
02405                    p2=p[q+1:]
02406 
02407               obj[p1]=p2
02408               if p1=='cm_console': con=p2
02409 
02410     return {'cm_return':0, 'cm_array':obj, 'cm_console':con}
02411 
02412 # ============================================================================
02413 def convert_text_to_value(i):
02414     """
02415     Convert cm text to cm value
02416 
02417     Input:  {
02418               cm_text - text to convert
02419               cm_type - type: {float, integer, string, uoa}
02420 
02421     Output: {
02422               cm_return - return code = 0 if successful
02423               cm_value  - value in python format
02424             }
02425     """
02426 
02427     v=None
02428 
02429     if 'cm_text' not in i: return {'cm_return':1, 'cm_error':'"cm_text" is not defined in kernel/convert_text_to_value'}
02430     if 'cm_type' not in i: return {'cm_return':1, 'cm_error':'"cm_type" is not defined in kernel/convert_text_to_value'}
02431 
02432     t=i['cm_text']
02433     tp=i['cm_type']
02434 
02435     if tp=='float':
02436        try: 
02437           v=float(t)
02438        except ValueError:
02439           return {'cm_return':16, 'cm_error':'can\'t convert '+str(t)+' to float'}
02440     elif tp=='integer':
02441        try: 
02442           v=int(t)
02443        except ValueError:
02444           return {'cm_return':16, 'cm_error':'can\'t convert '+str(t)+' to int'}
02445     elif tp=='uoa' or tp=='string' or tp=='text':
02446        v=str(t)
02447     else:
02448        return {'cm_return':16, 'cm_error':'can\'t recognize type '+tp}
02449 
02450     return {'cm_return':0, 'cm_value':v}
02451 
02452 # ============================================================================
02453 def find_keys_by_prefix(i):
02454     """
02455     Find keys by prefix
02456 
02457     Input:  {
02458               cm_array         - array to search
02459               cm_prefix        - prefix to search
02460               cm_sort_key_int  - sort key (int)
02461               cm_sort_key_text - sort key (text)
02462             }
02463 
02464     Output: {
02465               cm_return - return code = 0 if successful
02466               keys      - list of keys
02467             }
02468     """
02469 
02470     prefix=i.get('cm_prefix','')
02471     ski=i.get('cm_sort_key_int','')
02472     skt=i.get('cm_sort_key_text','')
02473 
02474     keys=[]
02475 
02476     a=i.get('cm_array',{})
02477     if ski!='':
02478        b=sorted(a.keys(), key=lambda k: (int(a[k].get(ski,'999999'))))
02479     elif skt!='':
02480        b=sorted(a.keys(), key=lambda k: (a[k].get(skt,'').lower()))
02481     else:
02482        b=a
02483 
02484     for k in b:
02485         if k.startswith(prefix): keys.append(k)
02486 
02487     return {'cm_return':0, 'cm_keys':keys}
02488 
02489 # ============================================================================
02490 def merge_arrays_ll(a, b):
02491     """
02492     Intelligently merge cm_array with cm_array1
02493 
02494     Input:  a  - merge this array with cm_array1 (will be directly modified!)
02495             b - cM array
02496 
02497     Output: {
02498               cm_return - return code = 0 if successful
02499             }
02500     """
02501     return merge_arrays({'cm_array':a, 'cm_array1':b})
02502 
02503 # ============================================================================
02504 def merge_arrays(i):
02505     """
02506     Intelligently merge cm_array with cm_array1
02507 
02508     Input:  {
02509               cm_array  - merge this array with cm_array1 (will be directly modified!)
02510               cm_array1 - cM array
02511 
02512     Output: {
02513               cm_return - return code = 0 if successful
02514               cm_array  - output array
02515             }
02516     """
02517 
02518     if 'cm_array' not in i: return {'cm_return':1, 'cm_error':'"cm_array" is not defined in "cm_kernel merge_arrays"'}
02519     if 'cm_array1' not in i: return {'cm_return':1, 'cm_error':'"cm_array1" is not defined in "cm_kernel merge_arrays"'}
02520 
02521     a=i['cm_array']
02522     b=i['cm_array1']
02523 
02524     for k in b:
02525         v=b[k]
02526         if type(v) is dict:
02527            if k not in a:
02528               a.update({k:b[k]})
02529            elif type(a[k])==dict:
02530               merge_arrays({'cm_array':a[k], 'cm_array1':b[k]})
02531            else:
02532               a[k]=b[k]
02533         elif type(v) is list:
02534 #           if k not in a or type(a[k]) is not list: 
02535 #              a[k]=[]
02536            a[k]=[]
02537            for y in v:
02538                a[k].append(y)
02539         else:
02540            a[k]=b[k]
02541 
02542     return {'cm_return':0, 'cm_array':a}
02543 
02544 # ============================================================================
02545 def str2cm(i):
02546     """
02547     Convert string to cM array
02548 
02549     Input:  string
02550 
02551     Output: {
02552               cm_return - return code = 0 if successful
02553               cm_array  - array
02554             }
02555     """
02556     # FGG: I changed it to shlex to be able to ignore spaces in quotes
02557 #    a=i.split(' ')
02558     a=shlex.split(i)
02559     return array2cm(a)
02560 
02561 # ============================================================================
02562 def array2str(i):
02563     """
02564     Convert array to string with space
02565 
02566     Input:  array
02567 
02568     Output: {
02569               cm_return - 0
02570               cm_string - string
02571             }
02572     """
02573     s=string.join(i, ' ')
02574     return {'cm_return':0, 'cm_string':s}
02575 
02576 # ============================================================================
02577 def get_var(a,p):
02578     """
02579     Check if parameter 'p' is in array 'a' and return a[p] or '' otherwise
02580 
02581     Input:  a - array
02582             p - parameter
02583 
02584     Output: a[p], if 'p' in 'a'
02585             '', if 'p' not in 'a'
02586 
02587     """
02588 
02589     if p not in a: return ''
02590     return a[p]
02591 
02592 # ============================================================================
02593 def prepare_final_out(i, j):
02594     """
02595     Prepare final output of cM access function depending on cm_console
02596 
02597     Input:  i= {
02598                  cm_return    - return code (check "access" function for full description)
02599                  (cm_error)   - error text  (check "access" function for full description)
02600                  ...
02601                }
02602 
02603             j= {
02604                  (cm_console) - cm_console  (check "access" function for full description)
02605                  (cm_output)  - if !='', save output to file
02606                  ...
02607                }
02608 
02609     Output: {}
02610 
02611     """
02612 
02613     # Prepare output if error
02614     if i['cm_return']>0:
02615        if j.get('cm_console','')=='txt': print_for_con('cM error: '+i['cm_error']+'!')
02616        if j.get('cm_console','')=='web': print_for_web('<br><pre>cM error: '+i['cm_error']+'!</pre><br>')
02617 
02618     # Check if record to file
02619     if j.get('cm_output','')!='':
02620        rx=save_array_to_file_as_json({'cm_filename':j['cm_output'], 'cm_array':i})
02621        if rx['cm_return']>0: 
02622           print_for_con('cM error: '+rx['cm_error']+'!')
02623 
02624     # In any case, if json, print json
02625     if j.get('cm_console','')=='json': 
02626        print_for_con(json.dumps(i))
02627     if j.get('cm_console','')=='json_after_text': 
02628        print_for_con(cm_json_with_text_sep)
02629        print_for_con(json.dumps(i))
02630     elif j.get('cm_console','')=='json_with_indent': 
02631        print_for_con(json.dumps(i, indent=2, sort_keys=True))
02632 
02633     return {}
02634 
02635 # ============================================================================
02636 def check_cm_json(i):
02637     """
02638     Check if 'cm_json' exist in array and decode it
02639 
02640     Input:  {
02641               cm_json - array to check
02642             } 
02643 
02644     Output: {...} - returned array where cm_json is decoded
02645     """
02646 
02647     ii={}
02648     ii.update(i)
02649 
02650     if 'cm_json' in i:
02651        s=i['cm_json']
02652        s=s.replace('^22^', '"')
02653        s=s.replace('%22', '"')
02654        s=s.replace('%26quot;', '"')
02655        s=s.replace('&quot;', '"')
02656        success=False
02657        try:
02658           j=json.loads(s)
02659           success=True
02660        except:
02661           pass
02662        if success:
02663           del (ii['cm_json'])
02664           ii.update(j)
02665 
02666     return ii
02667 
02668 # ============================================================================
02669 def convert_cm_array_to_uri(i):
02670     """
02671     Convert cM array to uri (convert various special parameters)
02672 
02673     Input:  {
02674               cm_array   - array to convert to uri
02675             }
02676 
02677     Output: {
02678               cm_return  - return code (check "access" function for full description)
02679               (cm_error) - error text  (check "access" function for full description)
02680               cm_string  - converted string
02681               cm_string1 - converted string (for urlopen)
02682             }
02683     """
02684 
02685     if 'cm_array' not in i:
02686        return {'cm_return':1, 'cm_error':'"cm_array" is not set in kernel/convert_cm_array_to_uri'}
02687 
02688     ca=i['cm_array']
02689     cca=copy.deepcopy(ca)
02690 
02691     if 'cm_user_uoa' in cca: del(cca['cm_user_uoa'])
02692 
02693     s=json.dumps(cca)
02694     s=s.replace('"', '^22^')
02695     s=s.replace('#', '%23')
02696 
02697     s=s.replace('+', '%2B')
02698 
02699     s1=s.replace('>', '%3E')
02700     s1=s1.replace('<', '%3C')
02701 
02702     s1=s1.replace('[', '%5B')
02703     s1=s1.replace(']', '%5D')
02704     s1=s1.replace(' ', '%20')
02705 
02706     s=cgi.escape(s)
02707 
02708     return {'cm_return':0, 'cm_string':s, 'cm_string1':s1}
02709 
02710 # ============================================================================
02711 def copy_vars(i):
02712     """
02713     Convert cM array to uri (convert various special parameters)
02714 
02715     Input:  {
02716               cm_input_array  - input array
02717               cm_output_array - output array
02718               cm_vars         - list of variables to copy
02719             }
02720 
02721     Output: {
02722               cm_return  - return code (check "access" function for full description)
02723             }
02724     """
02725 
02726     if 'cm_input_array' not in i: return {'cm_return':1, 'cm_error':'"cm_input_array" is not defined in kernel/copy_vars'}
02727     if 'cm_output_array' not in i: return {'cm_return':1, 'cm_error':'"cm_output_array" is not defined in kernel/copy_vars'}
02728     if 'cm_vars' not in i: return {'cm_return':1, 'cm_error':'"cm_vars" is not defined in kernel/copy_vars'}
02729 
02730     inp=i['cm_input_array']
02731     out=i['cm_output_array']
02732 
02733     vr=i['cm_vars']
02734 
02735     inp1=inp.keys()
02736     if len(vr)>0: inp1=vr
02737 
02738     for x in inp1:
02739         if x in inp: out[x]=inp[x]
02740 
02741     return {'cm_return':0}
02742 
02743 # ============================================================================
02744 def convert_str_to_special(s):
02745     """
02746     Convert string to a special cM string that is used to substitute variables in text
02747 
02748     Input:  string
02749 
02750     Output: special string 
02751     """
02752 
02753     return '$#'+s+'#$'
02754 
02755 # ============================================================================
02756 def get_cm_version(i):
02757 
02758     """
02759     Get cM version
02760 
02761     Input:  {
02762             }
02763 
02764     Output: {
02765               cm_return  - return code >0 if not authentificated
02766               cm_array   - array with version
02767               cm_string  - version
02768             }
02769     """
02770 
02771     a={}
02772     x=''
02773 
02774     if 'cm_version' in ini['dcfg']:
02775        a=ini['dcfg']['cm_version']
02776  
02777     if 'major' in a: x+=str(a['major'])
02778     if 'minor' in a: x+='.'+str(a['minor'])
02779     if 'build' in a: x+='.'+str(a['build'])
02780     if 'extra' in a: x+='.'+str(a['extra'])
02781 
02782     return {'cm_return':0, 'cm_array':a, 'cm_string':x}
02783 
02784 # ============================================================================
02785 def convert_str_to_sha1(i):
02786 
02787     """
02788     Convert string to sha1
02789 
02790     Input:  {
02791               cm_string       - string
02792             }
02793 
02794     Output: {
02795               cm_return             - return code >0 if not authentificated
02796               cm_string_sha1        - password in SHA1 (digest)
02797               cm_string_sha1_hex    - password in SHA1 (digest in hex)
02798               cm_string_sha1_base64 - BASE64 (SHA1 digest) - compatible with htpasswd format
02799             }
02800     """
02801 
02802     if 'cm_string' not in i: 
02803        return {'cm_return':1, 'cm_error':'"cm_string" is not set'}
02804 
02805     x=hashlib.sha1()
02806     x.update(i['cm_string'])
02807     y=x.digest().strip()
02808     z=x.hexdigest().strip()
02809     b=base64.encodestring(y).strip()
02810 
02811     return {'cm_return':0, 'cm_string_sha1':y, 'cm_string_sha1_hex':z, 'cm_string_sha1_base64':b}
02812 
02813 # ============================================================================
02814 def prepare_cid(i):
02815 
02816     """
02817     Prepare cid 
02818 
02819     Input:  {
02820               (cm_repo_uoa)                     - repository UOA
02821               cm_run_module_uoa | cm_module_uoa - module UOA
02822               cm_data_uoa                       - data UOA
02823             }
02824 
02825     Output: {
02826               cm_return               - 0
02827               cm_string_uid           - cid with only uid
02828               cm_string_with_repo_uid - cid with only uid
02829               cm_string_uoa           - cid with alias or uid
02830               cm_string_with_repo_uoa - cid with alias or uid
02831               cm_repo_uid             - cm_repo_uid
02832               cm_repo_uoa             - cm_repo_uoa
02833               cm_module_uid           - cm_module_uid
02834               cm_module_uoa           - cm_module_uoa
02835               cm_data_uid             - cm_data_uid
02836               cm_data_uoa             - cm_data_uoa
02837             }
02838     """
02839 
02840     cm_repo_uid=''
02841     cm_repo_uoa=''
02842     cm_module_uid=''
02843     cm_module_uoa=''
02844     cm_data_uid=''
02845     cm_data_uoa=''
02846 
02847     # Check repository
02848     if 'cm_repo_uoa' in i and i['cm_repo_uoa']!='':
02849        ii={'cm_run_module_uoa':ini['dcfg']['cmr_repo'],
02850            'cm_data_uoa':i['cm_repo_uoa'],
02851            'cm_action':'load'}
02852        r=cm_kernel.access(ii)
02853        if r['cm_return']==0:
02854           cm_repo_uid=r['cm_uid']
02855           cm_repo_uoa=cm_repo_uid
02856           if r['cm_alias']!='': cm_repo_uoa=r['cm_alias']
02857        else:
02858           cm_repo_uoa=i['cm_repo_uoa']
02859 
02860     # Check module
02861     if 'cm_module_uoa' in i and i['cm_module_uoa']!='':
02862        ii={'cm_run_module_uoa':ini['dcfg']['cmr_module'],
02863            'cm_data_uoa':i['cm_module_uoa'],
02864            'cm_action':'load'}
02865        r=cm_kernel.access(ii)
02866        if r['cm_return']==0:
02867           cm_module_uid=r['cm_uid']
02868           cm_module_uoa=cm_module_uid
02869           if r['cm_alias']!='': cm_module_uoa=r['cm_alias']
02870        else:
02871           cm_module_uoa=i['cm_module_uoa']
02872 
02873     # Check data
02874     if 'cm_data_uoa' in i and i['cm_data_uoa']!='':
02875        ii={'cm_run_module_uoa':i['cm_module_uoa'],
02876            'cm_data_uoa':i['cm_data_uoa'],
02877            'cm_action':'load'}
02878        r=cm_kernel.access(ii)
02879        if r['cm_return']==0:
02880           cm_data_uid=r['cm_uid']
02881           cm_data_uoa=cm_data_uid
02882           if r['cm_alias']!='': cm_data_uoa=r['cm_alias']
02883        else:
02884           cm_data_uoa=i['cm_data_uoa']
02885 
02886     # Prepare cid
02887     r={'cm_return':0}
02888     r['cm_string_uid']=cm_module_uid+':'+cm_data_uid
02889     if cm_repo_uid!='': r['cm_string_with_repo_uid']=cm_repo_uid+':'+r['cm_string_uid']
02890     r['cm_string_uoa']=cm_module_uoa+':'+cm_data_uoa
02891     if cm_repo_uoa!='': r['cm_string_with_repo_uoa']=cm_repo_uoa+':'+r['cm_string_uoa']
02892 
02893     r['cm_repo_uid']=cm_repo_uid
02894     r['cm_repo_uoa']=cm_repo_uoa
02895     r['cm_module_uid']=cm_module_uid
02896     r['cm_module_uoa']=cm_module_uoa
02897     r['cm_data_uid']=cm_data_uid
02898     r['cm_data_uoa']=cm_data_uoa
02899 
02900     return r
02901 
02902 # ============================================================================
02903 def access_control(i):
02904 
02905     """
02906     Control access 
02907 
02908     Input:  {
02909               dcfg                    - cfg of the current entry
02910               module_uoa              - module UOA of the entry
02911               module_uid              - module UID of the entry
02912               data_uoa                - data UOA of the entry
02913               key                     - 'read' or 'write'
02914             }
02915 
02916     Output: {
02917               cm_return - 0, if Ok; ==32 if access denied
02918               cm_error  - if access is denied
02919             }
02920     """
02921 
02922     cm_user_uid=ini['web_auth'].get('cm_user_uid','')
02923     cm_user_uoa=ini['web_auth'].get('cm_user_uoa','')
02924     xcfg=i.get('dcfg',{})
02925 
02926     module_uoa=i.get('module_uoa','')
02927     module_uid=i.get('module_uid','')
02928     data_uoa=i.get('data_uoa','')
02929 
02930     key=i.get('key','')
02931 
02932     ac=xcfg.get('access_control',{})
02933 
02934     aread=xcfg.get('cm_access_control',{}).get(key+'_groups','')
02935 
02936     access=False
02937 
02938     if aread=='all':
02939        access=True
02940     elif cm_user_uoa!='' and cm_user_uid!='':
02941        if cm_user_uoa in ini['dcfg'].get('admin_users',[]) or \
02942           cm_user_uid in ini['dcfg'].get('admin_users',[]):
02943           access=True
02944        else:
02945           access=False
02946 
02947           # Check if module=user or organization, i.e. accessing own user params
02948           if module_uid==ini['dcfg']['cm_user_module_uoa'] and \
02949              data_uoa==cm_user_uoa or data_uoa==cm_user_uid:
02950              access=True
02951           else:   
02952              # Check if allowed for registered
02953              if aread=='registered':
02954                 access=True
02955              elif aread=='owner':
02956                 # Otherwise check if owner
02957                 cmu=xcfg.get('cm_updated',[])
02958                 if len(cmu)>0:
02959                    owner_uoa=cmu[0].get('cm_user_uoa','')
02960                    if owner_uoa==cm_user_uoa or owner_uoa==cm_user_uid:
02961                       access=True
02962 
02963     else:
02964        # If no user
02965        if aread=='' or aread=='registered' or aread=='owner':
02966           access=False
02967     
02968     if not access:
02969        if ini['web_auth']['cm_user_status']=='failed':
02970           return {'cm_return':32, 'cm_error':key+' access denied to "'+module_uoa+':'+data_uoa+'" (user authentication failed: '+ini['web_auth']['cm_error']+')!'}
02971        else:
02972           return {'cm_return':32, 'cm_error':key+' access denied to "'+module_uoa+':'+data_uoa+'" (user likely have to be registered, be administrator or owner of this data)!'}
02973 
02974     return {'cm_return':0}
02975 
02976 # ============================================================================
02977 def auth_user(i):
02978 
02979     """
02980     Authenticate user
02981 
02982     Input:  {
02983               cm_user_uoa       - user UOA
02984               cm_user_password  - user password in plain text 
02985                                   (not secure, usually used for web form authentication)
02986               cm_user_password1 - user password in SHA1
02987                                   (more secure, can be stored in environment, session or cookies)
02988                                   cM authenicates by taking SHA1(cm_user_password1)...
02989               cm_user_password2 - only should be set here by web environment!
02990             }   
02991 
02992     Output: {
02993               cm_return            - return code = 0 if authenticated
02994               (cm_error)           - error text, if cm_return > 0
02995               cm_user_cfg          - user config if authenticated (to check groups, etc)
02996               cm_user_password2    - user password in SHA1(SHA1)
02997               cm_user_uid          - user UID
02998               cm_user_uoa          - user UOA
02999               cm_user_alias        - user alias
03000               cm_user_path         - user path
03001             }
03002     """
03003 
03004     # Unsafe to use environment for passwords but may be still needed
03005     #  for automatic experimentation on cloud/grid/supercomputers 
03006     # combined with remote aggregation of experiments
03007 
03008     # If no user/pass, try from environment
03009     if 'cm_user_uoa' not in i or i['cm_user_uoa']=='':
03010        if env_cm_user_uoa in os.environ.keys():
03011           i['cm_user_uoa']=os.environ[env_cm_user_uoa].strip()
03012 
03013     if 'cm_user_password' not in i or i['cm_user_password']=='':
03014        if env_cm_user_password in os.environ.keys():
03015           i['cm_user_password']=os.environ[env_cm_user_password].strip()
03016 
03017     if 'cm_user_password1' not in i or i['cm_user_password1']=='':
03018        if env_cm_user_password1 in os.environ.keys():
03019           i['cm_user_password1']=os.environ[env_cm_user_password1].strip()
03020 
03021     # Check vars
03022     if 'cm_user_uoa' not in i or i['cm_user_uoa']=='':
03023        return {'cm_return':127, 'cm_error': '"cm_user_uoa" is not set in auth_user'}
03024 
03025     # Check if passwords are in set
03026     pwd1=''
03027     pwd2=''
03028     if 'cm_user_password' in i and i['cm_user_password']!='':
03029        r=convert_str_to_sha1({'cm_string':i['cm_user_password']})
03030        if r['cm_return']>0: return r
03031        pwd1=r['cm_string_sha1_base64']
03032     elif 'cm_user_password1' in i and i['cm_user_password1']!='':
03033        pwd1=i['cm_user_password1']
03034     elif 'cm_user_password2' in i and i['cm_user_password2']!='':
03035        pwd2=i['cm_user_password2']
03036 
03037     if pwd2=='':
03038        if pwd1=='':
03039           return {'cm_return':127, 'cm_error':'authentication is required for this operation, but password is not set'}
03040 
03041        # Convert to SHA1 again!
03042        r=convert_str_to_sha1({'cm_string':pwd1})
03043        if r['cm_return']>0: return r
03044        pwd2=r['cm_string_sha1_base64']
03045 
03046     # Load user
03047     r=cm_kernel.access({'cm_run_module_uoa':ini['dcfg']['cm_user_module_uoa'],
03048                         'cm_action':'load',
03049                         'cm_data_uoa':i['cm_user_uoa'],
03050                         'cm_admin':'yes'})
03051     if r['cm_return']>0: 
03052        if r['cm_return']==16: r={'cm_return':16, 'cm_error':'can\'t find user "'+i['cm_user_uoa']+'"'}
03053        return r
03054 
03055     user_cfg=r['cm_data_obj']['cfg']
03056     user_path=r['cm_path']
03057     user_uid=r['cm_uid']
03058     user_uoa=r['cm_uoa']
03059     user_alias=r['cm_alias']
03060 
03061     if r['cm_uoa'] in ini['dcfg'].get('admin_users',[]) or \
03062        r['cm_uid'] in ini['dcfg'].get('admin_users',[]):
03063        ini['web_auth']['cm_admin']='yes'
03064 
03065     # Check if user entry has a password
03066     if 'cm_user_password2' not in user_cfg:
03067        return {'cm_return':127, 'cm_error':'user "'+i['cm_user_uoa']+'" doesn\'t have a password'}
03068 
03069     # Check password
03070     if pwd2!=user_cfg['cm_user_password2']:
03071        return {'cm_return':127, 'cm_error':'authentication failed (passwords do not match)'}
03072 
03073     # Authenticated fine, return config of a user (to know which groups belong to, etc)
03074     return {'cm_return':0, 'cm_user_cfg':user_cfg, 'cm_user_password2':pwd2, 'cm_user_uid':user_uid, 
03075                            'cm_user_uoa':user_uoa, 'cm_user_alias':user_alias, 'cm_user_path':user_path}
03076 
03077 # ============================================================================
03078 def auth_user_and_ini_cm(i):
03079 
03080     """
03081     Check if need to auth user, try to auth and set cM vars
03082 
03083     Input:  {
03084               cm_user_uoa       - user UOA
03085               cm_user_password  - user password in plain text 
03086                                   (not secure, usually used for web form authentication)
03087               cm_user_password1 - user password in SHA1
03088                                   (more secure, can be stored in environment, session or cookies)
03089                                   cM authenicates by taking SHA1(cm_user_password1)...
03090               cm_user_password2 - only should be set here by web environment!
03091             }   
03092 
03093     Output: {
03094               cm_return            - return code = 0 if authenticated
03095               (cm_error)           - error text, if cm_return > 0
03096 
03097                If needed to auth or default user:
03098               cm_user_cfg          - user config if authenticated (to check groups, etc)
03099               cm_user_password1    - user password in SHA1
03100               cm_user_uid          - user UID
03101               cm_user_uoa          - user UOA
03102               cm_user_alias        - user alias
03103               cm_user_path         - user path
03104               cm_user_status       - 'no_user', 'default', 'logged_in' or 'failed'
03105             }
03106     """
03107 
03108     rx={'cm_return':0}
03109 
03110     # First set default user
03111     ini['web_auth']['cm_user_status']='no_user'
03112     if (i.get('cm_web','')!='yes' or ini['dcfg'].get('force_web_login','')!='yes') and \
03113         ini['dcfg'].get('cm_default_user_uoa','')!='':
03114 
03115        # Load default user using admin privileges
03116        user=ini['dcfg']['cm_default_user_uoa']
03117        r=access({'cm_run_module_uoa':ini['dcfg']['cm_user_module_uoa'],
03118                  'cm_action':'load',
03119                  'cm_data_uoa':user,
03120                  'cm_admin':'yes'})
03121        if r['cm_return']>0: 
03122           if r['cm_return']==16: r={'cm_return':16, 'cm_error':'can\'t load default user "'+user+' - please, run configure.sh"'}
03123           return r
03124 
03125        rx={'cm_user_cfg':r['cm_data_obj']['cfg'],
03126            'cm_user_uid':r['cm_uid'],
03127            'cm_user_uoa':r['cm_uoa'],
03128            'cm_user_alias':r['cm_alias'],
03129            'cm_user_path':r['cm_path'],
03130            'cm_user_status':'default'}
03131 
03132        ini['web_auth']=copy.deepcopy(rx)
03133        rx['cm_return']=0
03134 
03135        if r['cm_uoa'] in ini['dcfg'].get('admin_users',[]) or \
03136           r['cm_uid'] in ini['dcfg'].get('admin_users',[]):
03137           ini['web_auth']['cm_admin']='yes'
03138 
03139     # Try to auth if needed (check that not default user if not strict login)
03140     if i.get('cm_web','')=='yes':
03141        if (ini['dcfg'].get('force_web_login','')=='yes' or \
03142            ini['dcfg'].get('allow_web_user_auth','')=='yes'):
03143 
03144           ini['web_auth']['cm_admin']='no'
03145 
03146           if ini['dcfg'].get('force_web_login','')!='yes' and \
03147               (i.get('cm_user_uoa','')!='' and ini['dcfg'].get('cm_default_user_uoa','')!='' and \
03148               (i['cm_user_uoa']==ini['web_auth']['cm_user_uid'] or i['cm_user_uoa']==ini['web_auth']['cm_user_alias'])):
03149              ini['web_auth']['cm_error']='user already logged in'
03150 
03151              if i['cm_user_uoa'] in ini['dcfg'].get('admin_users',[]):
03152                 ini['web_auth']['cm_admin']='yes'
03153 
03154           else:
03155              rx=auth_user(i)
03156              if rx['cm_return']>0: 
03157 #                if ini['dcfg'].get('force_web_login','')=='yes':
03158                 ini['web_auth']['cm_user_status']='failed'
03159                 ini['web_auth']['cm_error']=rx['cm_error']
03160              else:
03161                 ini['web_auth'].update(rx)
03162                 ini['web_auth']['cm_user_status']='logged_in'
03163 
03164                 if rx['cm_user_uoa'] in ini['dcfg'].get('admin_users',[]) or \
03165                    rx['cm_user_uid'] in ini['dcfg'].get('admin_users',[]):
03166                    ini['web_auth']['cm_admin']='yes'
03167 
03168           rx['cm_return']=0 # web front-end will be dealing with failed logins ...
03169 
03170     # Check if failed
03171     if ini['web_auth'].get('cm_user_status','')=='failed' and \
03172        ini['dcfg'].get('cm_default_user_uoa','')!='':
03173 
03174        # Load default user using admin privileges
03175        user=ini['dcfg']['cm_default_user_uoa']
03176        r=access({'cm_run_module_uoa':ini['dcfg']['cm_user_module_uoa'],
03177                  'cm_action':'load',
03178                  'cm_data_uoa':user,
03179                  'cm_admin':'yes'})
03180        if r['cm_return']>0: 
03181           if r['cm_return']==16: r={'cm_return':16, 'cm_error':'can\'t load default user "'+user+' - please, run configure.sh"'}
03182           return r
03183 
03184        rx={'cm_user_cfg':r['cm_data_obj']['cfg'],
03185            'cm_user_uid':r['cm_uid'],
03186            'cm_user_uoa':r['cm_uoa'],
03187            'cm_user_alias':r['cm_alias'],
03188            'cm_user_path':r['cm_path'],
03189            'cm_user_status':'default'}
03190 
03191        ini['web_auth']=copy.deepcopy(rx)
03192        rx['cm_return']=0
03193 
03194        if r['cm_uoa'] in ini['dcfg'].get('admin_users',[]) or \
03195           r['cm_uid'] in ini['dcfg'].get('admin_users',[]):
03196           ini['web_auth']['cm_admin']='yes'
03197 
03198     # Set default web style (either from kernel or from user)
03199     cm_default_web_uoa=''
03200     if i.get('cm_web_uoa','')!='':
03201        cm_default_web_uoa=i['cm_web_uoa']
03202     else:
03203        cm_default_web_uoa=''
03204        if ini['dcfg'].get('cm_default_web_uoa','')!='': 
03205           cm_default_web_uoa=ini['dcfg']['cm_default_web_uoa']
03206 
03207 # Should not do that - we should move only styles to web.cfg in the future
03208 #       # Get web template from user or default
03209 #       if 'cm_user_cfg' in ini['web_auth'] and \
03210 #          'cm_default_web_uoa' in ini['web_auth']['cm_user_cfg'] and \
03211 #          ini['web_auth']['cm_user_cfg']['cm_default_web_uoa']!='':
03212 #          cm_default_web_uoa=ini['web_auth']['cm_user_cfg']['cm_default_web_uoa']
03213 
03214     # Set and load
03215     if cm_default_web_uoa=='': cm_default_web_uoa=var_cm_cfg_default
03216 
03217     ini['web_style']['uoa']=cm_default_web_uoa
03218 
03219     r=cm_kernel.access({'cm_run_module_uoa':ini['dcfg']['cm_web_module_uoa'],
03220                         'cm_action':'load',
03221                         'cm_data_uoa':cm_default_web_uoa,
03222                         'cm_admin':'yes'})
03223     if r['cm_return']>0: return r
03224 
03225     ini['web_style']['cfg']=r['cm_data_obj']['cfg']
03226     ini['web_style']['path']=r['cm_path']
03227 
03228     return rx
03229 
03230 # ============================================================================
03231 def access_fe_as_string(s):
03232     """
03233     Front-end to single entry 'access' function as string (if someone finds it easier).
03234 
03235     Input:  string that will be converted to array (See "access" function).
03236     Output: See "access" function.
03237     """
03238 
03239     r=str2cm(s)
03240     if r['cm_return']>0: return r
03241 
03242     return access(r['cm_array'])
03243 
03244 # ============================================================================
03245 def access_fe_through_cmd(i):
03246     """
03247     FGG TBD: Currently used only in cM web server.
03248              Should be cleaned and updated!
03249              Should check that tmp files are deleted!
03250 
03251     Main function to access cM through a command line (useful for web server, etc).
03252 
03253     Input:  {
03254                ...                 - see "access" function from this module
03255                (cm_detach_console) - if 'yes', detach console (for installations or experiments)
03256             }
03257     Output: {
03258               cm_return - return code = 0 if successful
03259               cm_stdout - stdout 
03260               cm_stderr - stderr
03261             }
03262     """
03263 
03264     # Check if cM module is in the input
03265     if 'cm_run_module_uoa' not in i:
03266        return {'cm_return':1, 'cm_error':'no module specified'}
03267     module=i['cm_run_module_uoa']
03268 
03269     # Check if cM action is in the input
03270     if 'cm_action' not in i:
03271        return {'cm_return':1, 'cm_error':'no action specified for module '+i['cm_run_module_uoa']}
03272     action=i['cm_action']
03273 
03274     cm_stdout=''
03275     cm_stderr=''
03276 
03277     i1={}; i1.update(i)
03278     del (i1['cm_run_module_uoa'])
03279     del (i1['cm_action'])
03280 
03281     if i.get('cm_detach_console','')=='yes': 
03282        # Force text mode
03283        i1['cm_console']='txt'
03284        if 'cm_web' in i1: del(i1['cm_web'])
03285 
03286     rr={}; 
03287     rr['cm_stdout']=''
03288     rr['cm_stderr']=''
03289 
03290     # Save json to temporay file
03291     rx=cm_kernel.gen_cm_tmp_file({})
03292     if rx['cm_return']>0: 
03293        rr['cm_return']=rx['cm_return']
03294        rr['cm_stderr']=rx['cm_error']
03295        return rr
03296     tf=rx['cm_path']
03297 
03298     r=save_array_to_file_as_json({'cm_filename':tf, 'cm_array':i1})
03299     if r['cm_return']>0:
03300        rr['cm_return']=r['cm_return']
03301        rr['cm_stderr']=r['cm_error']
03302        return rr
03303 
03304     cmd='cm '+module+' '+action+' @'+tf
03305     if i.get('cm_detach_console','')=='yes':
03306        # Check if detached console is forbidden (allow for admin)
03307        if ini['dcfg'].get('forbid_detached_console','')=='yes' or \
03308           (i.get('cm_web','')=='yes' and \
03309            ini['dcfg'].get('forbid_detached_console_in_web','')=='yes' and \
03310            i.get('cm_user_uoa','')!='' and i.get('cm_user_uoa','') not in ini['dcfg'].get('admin_users',[])):
03311           rr['cm_return']=0
03312           rr['cm_stdout']='<B>cM error:</B> Detaching console is explicitly forbidden in the kernel'
03313           return rr
03314 
03315        # Load OS to get cmd for detached console
03316        os_uoa=ini['dcfg'].get('cm_default_os_uoa','')
03317 
03318        if os_uoa=='':
03319           rr['cm_return']=1
03320           rr['cm_stderr']='detached console is requested but default os uoa is not in kernel'
03321           return rr
03322 
03323        ii={'cm_run_module_uoa':ini['dcfg']['cm_os_module_uoa'],
03324            'cm_data_uoa':os_uoa,
03325            'cm_action':'load'}
03326        rx=access(ii)
03327        if rx['cm_return']>0: 
03328           rr['cm_return']=rx['cm_return']
03329           rr['cm_stderr']=rx['cm_error']
03330           return rr
03331        dos=rx['cm_data_obj']['cfg']
03332 
03333        dcmd=dos.get('cmd_for_detached_console','')
03334        if dcmd=='':
03335           rr['cm_return']=1
03336           rr['cm_stderr']='detached console is requested but cmd is not defined in OS'
03337           return rr
03338 
03339        dcmd=dcmd.replace(convert_str_to_special('cmd'), cmd)
03340        if dos.get('use_create_new_console_flag','')=='yes':
03341           process=subprocess.Popen(dcmd, stdin=None, stdout=None, stderr=None, shell=True, close_fds=True, creationflags=subprocess.CREATE_NEW_CONSOLE)
03342        else:
03343           # Will need to do the forking
03344           try:
03345              pid=os.fork()
03346           except OSError, e:
03347              rr['cm_return']=1
03348              rr['cm_stderr']='forking detached console failed!'
03349              return rr
03350 
03351           if pid==0:
03352              os.setsid()
03353 
03354              pid=os.fork()
03355              if pid!=0: os._exit(0)
03356 
03357              try:
03358                  maxfd=os.sysconf("SC_OPEN_MAX")
03359              except (AttributeError, ValueError):
03360                  maxfd=1024
03361 
03362              for fd in range(maxfd):
03363                  try:
03364                     os.close(fd)
03365                  except OSError:
03366                     pass
03367 
03368              os.open('/dev/null', os.O_RDWR)
03369              os.dup2(0, 1)
03370              os.dup2(0, 2)
03371 
03372              # Normally child process
03373              process=os.system(dcmd)
03374              os._exit(0)
03375 
03376        if i.get('cm_console')=='web':
03377           cm_stdout=dos.get('html_for_detached_console','')
03378           cm_stderr=''
03379        else:
03380           cm_stdout='Console was detached!'
03381           cm_stderr=''
03382     else:
03383        process=subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
03384        cm_stdout,cm_stderr=process.communicate()
03385 
03386     rr['cm_return']=0
03387     rr['cm_stdout']=cm_stdout
03388     rr['cm_stderr']=cm_stderr
03389 
03390     return rr
03391 
03392 # ============================================================================
03393 def access_fe_from_cmd(a):
03394     """
03395     Front-end to single entry 'access' function from command line (bash, windows shell, etc)
03396     If cm_console=='', it will be set to 'txt'
03397     If cm_user_uoa and cm_user_password1 are not in input but in environment, they will be also used
03398 
03399     Input:  cmd list that will be converted to array (See "access" function)
03400     Output: See "access" function
03401     """
03402     r=array2cm(a)
03403     if r['cm_return']>0: 
03404        con=r['cm_console']
03405        if con=='': con='txt'
03406        prepare_final_out(r, {'cm_console':con, 'cm_output':r.get('cm_output','')})
03407        return r
03408 
03409     i=r['cm_array']
03410 
03411     # Check console
03412     if 'cm_console' not in i or i['cm_console']=='': i.update({'cm_console':'txt'})
03413 
03414 #    # If no user/pass, try from environment
03415 #    if 'cm_user_uoa' not in i or i['cm_user_uoa']=='':
03416 #       if env_cm_user_uoa in os.environ.keys():
03417 #          i['cm_user_uoa']=os.environ[env_cm_user_uoa].strip()
03418 #
03419 #    if 'cm_user_password1' not in i or i['cm_user_password1']=='':
03420 #       if env_cm_user_password1 in os.environ.keys():
03421 #          i['cm_user_password1']=os.environ[env_cm_user_password1].strip()
03422 
03423     return access_fe(i)
03424 
03425 # ============================================================================
03426 def access_fe(i):
03427 
03428     """
03429     Front-end to single entry 'access' function
03430     cM will be initialized if needed
03431 
03432     Input:  See "access" function
03433     Output: See "access" function
03434     """
03435 
03436     global ini
03437 
03438     # If error encountered during previous cM intialization, set error and quit
03439     if ini['init']==1:
03440        r={'cm_return':1, 'cm_error':'cM initialization failed', 'cm_console':i['cm_console']}
03441        prepare_final_out(r, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03442        return r
03443 
03444     # If cM is not yet initialized, perform initialization
03445     if ini['init']==-1:
03446        ini['init']=1 # Expect error and set to 0 if everything is fine
03447 
03448        r=init(i)
03449        if r['cm_return']>0: 
03450           prepare_final_out(r, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03451           return r
03452 
03453        ini['init']=0
03454 
03455     # Check if cM module is in the input
03456     if 'cm_run_module_uoa' not in i:
03457        r={'cm_return':1, 'cm_error':'no module specified'}
03458        prepare_final_out(r, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03459        return r
03460 
03461     # Check if cM action is in the input
03462     if 'cm_action' not in i:
03463        r={'cm_return':1, 'cm_error':'no action specified for module '+i['cm_run_module_uoa']}
03464        prepare_final_out(r, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03465        return r
03466 
03467     # Auth user if needed (do not fail here, just set vars that will be processed later)
03468     # If need to authenticate, forward to login page
03469 
03470     # Set default user
03471     rx=auth_user_and_ini_cm(i)
03472     if rx['cm_return']>0: 
03473        prepare_final_out(rx, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03474        return rx
03475 
03476     # Force removal of cm_user_password2 for security
03477     if i.get('cm_user_password2','')!='': del(i['cm_user_password2'])
03478 
03479     # If web environment, set web
03480     if i.get('cm_web')=='yes': ini['web']='yes'
03481 
03482     r=access(i)
03483 
03484     prepare_final_out(r, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03485     return r
03486 
03487 # ============================================================================
03488 def remote_access(i):
03489 
03490     """
03491     Remote access to cM
03492 
03493     Input:  {
03494               cm_remote_url             - remote URL
03495               (cm_user_uoa)             -
03496               (cm_user_password)        -
03497               (cm_user_password1)       -
03498               (remote_repo_uoa)
03499 
03500               (remote_pre_auth)         - if 'yes', need standard http login/password pre-authentication
03501               (cm_remote_user_name)     - remote username
03502               (cm_remote_user_password) - remote password
03503 
03504               (cm_web_module_uoa)       - module to run
03505               (cm_web_action)           - action to perform
03506                                           if =='download', prepare entry/file download through Internet
03507 
03508               (cm_save_to_file)         - if cm_web_action==download,
03509                                           save output to this file
03510 
03511               ...                       - all other request parameters
03512             }
03513 
03514     Output: {
03515               cm_return  - return code = 0 if successful
03516                                       > 0 if error
03517                                       < 0 if warning (rarely used at this moment)
03518               (cm_error) - error text, if cm_return > 0
03519             }
03520     """
03521 
03522     # Prepare request
03523     post=''
03524 
03525     url=i.get('cm_remote_url','')
03526     if url=='':
03527        return {'cm_return':1, 'cm_error':'cm_remote_url is not defined'}
03528     del(i['cm_remote_url'])
03529 
03530     if i.get('cm_user_uoa','')!='': 
03531        if post!='': post+='&'
03532        post+='cm_user_uoa='+i['cm_user_uoa']
03533        del(i['cm_user_uoa'])
03534 
03535     if i.get('cm_user_password','')!='': 
03536        if post!='': post+='&'
03537        post+='cm_user_password='+i['cm_user_password']
03538        del(i['cm_user_password'])
03539 
03540     if i.get('cm_user_password1','')!='': 
03541        if post!='': post+='&'
03542        post+='cm_user_password1='+i['cm_user_password1']
03543        del(i['cm_user_password1'])
03544 
03545     if i.get('remote_repo_uoa','')!='': 
03546        if post!='': post+='&'
03547        post+='cm_repo_uoa='+i['remote_repo_uoa']
03548        del(i['remote_repo_uoa'])
03549 
03550     # Check if needed remote pre-authentication
03551     base64string=''
03552     if i.get('remote_pre_auth', '')=='yes':
03553        del(i['remote_pre_auth'])
03554 
03555        username=''
03556        if i.get('cm_remote_user_name','')!='': 
03557           username=i['cm_remote_user_name']
03558           del(i['cm_remote_user_name'])
03559 
03560        password=''
03561        if i.get('cm_remote_user_password','')!='': 
03562           password=i['cm_remote_user_password']
03563           del(i['cm_remote_user_password'])
03564 
03565        base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
03566 
03567     # Check if data download, not json and convert it to download request
03568     download=False
03569     if i.get('cm_web_action','')=='download' or i.get('cm_web_action','')=='show':
03570        download=True
03571        if post!='': post+='&'
03572        post+='cm_web_module_uoa=web&cm_web_action='+i['cm_web_action']
03573        del(i['cm_web_action'])
03574        if i.get('cm_web_module_uoa','')!='': del(i['cm_web_module_uoa'])
03575        if i.get('cm_console','')!='': del(i['cm_console'])
03576     else:
03577        if post!='': post+='&'
03578        post+='cm_console=json'
03579 
03580     # Prepare array to transfer through Internet
03581     r=convert_cm_array_to_uri({'cm_array':i})
03582     if post!='': post+='&'
03583     post+='cm_json='+unicode(r['cm_string1'])
03584 
03585     # Prepare URL request
03586     request = urllib2.Request(url, post)
03587 
03588     if base64string!='':
03589        request.add_header("Authorization", "Basic %s" % base64string)   
03590 
03591     # Check if proxy is needed
03592     aproxy={}
03593     if env_cm_remote_repo_proxy_https in os.environ.keys():
03594        aproxy['https']=os.environ[env_cm_remote_repo_proxy_https].strip()
03595     if env_cm_remote_repo_proxy_http in os.environ.keys():
03596        aproxy['http']=os.environ[env_cm_remote_repo_proxy_http].strip()
03597 
03598     if len(aproxy)>0:
03599        proxy = urllib2.ProxyHandler(aproxy)
03600        opener = urllib2.build_opener(proxy)
03601        urllib2.install_opener(opener)
03602 
03603     # Connect
03604     try:
03605         f=urllib2.urlopen(request)
03606     except Exception as e:
03607         return {'cm_return':1, 'cm_error':'Remote access failed ('+format(e)+')'}
03608 
03609     # Read from Internet
03610     try:
03611         s=str(f.read())
03612         f.close()
03613     except Exception as e:
03614         return {'cm_return':1, 'cm_error':'Failed reading stream from remote server ('+format(e)+')'}
03615 
03616     request=None
03617 
03618     r={'cm_return':0}
03619 
03620     # Check if download, not json!
03621     if download:
03622        name='default_download_name.dat'
03623 
03624        if i.get('cm_web_filename','')!='':
03625           name=os.path.basename(i['cm_web_filename'])
03626        if i.get('cm_save_to_file','')!='': 
03627           # FGG: unsecure - should add checks where to write and what
03628           # to avoid overwriting some important data or put code somewhere
03629           name=i['cm_save_to_file']
03630 
03631        try:
03632           f=open(name,'wb')
03633           f.write(s)
03634           f.close()
03635        except Exception as e:
03636           return {'cm_return':1, 'cm_error':'problem saving downloaded file ('+format(e)+')'}
03637 
03638     else:
03639        try:
03640           r=json.loads(s)
03641        except:
03642           return {'cm_return':32, 'cm_error':'Can\'t decode JSON ('+s+')'}
03643 
03644     return r
03645 
03646 # ============================================================================
03647 def access(i):
03648 
03649     """
03650     Single entry point to access all cM functions.
03651 
03652     Input:  {
03653               Set of global cM variables:
03654 
03655                 cm_run_module_uoa - UOA of the module to run
03656                 cm_action         - module action (function)
03657 
03658                 (cm_console)       - by default, do not output anything except errors
03659                                      if 'txt' then output as plain text
03660                                      if 'web' then output for web (html, image, etc)
03661                                      if 'json' then output as json
03662 
03663                 cm_web            - if 'yes', function was called from web environment
03664                 cm_web_preprocess - if 'yes', we in the preprocessing stage in the web environment,
03665                                     where we can still change the http headers, etc ...
03666 
03667                 If cM Web access or fully remote access:
03668                 cm_user_uoa       - user UOA
03669 
03670                                     If requires user authentication in web environment!
03671                                     After authentication, passwords will be removed from the array
03672 
03673                 cm_user_password  - user password in plain text 
03674                                     (not secure, usually used for web form authentication)
03675                 cm_user_password1 - user password in SHA1
03676                                     (more secure, can be stored in environment, session or cookies)
03677                                      cM authenicates by taking SHA1(cm_user_password1)...
03678 
03679                 If access to remote repository or fully remote access:
03680 
03681                 cm_remote_user_uoa      - remote user UOA
03682                 cm_remote_user_password - remote user password
03683 
03684               Set of parameters depending on the called module and action (function)
03685             }
03686 
03687     Output: {
03688               cm_return  - return code = 0 if successful
03689                                       > 0 if error
03690                                       < 0 if warning (rarely used at this moment)
03691               (cm_error) - error text, if cm_return > 0
03692 
03693               Set of output parameters depending on the called module and action (function)
03694             }
03695     """
03696 
03697     # Check if local access (possibly with remote repository)
03698     if 'cm_run_module_uoa' not in i: return {'cm_return':1, 'cm_error':'"cm_run_module_uoa" is not set'}
03699     if 'cm_action' not in i: return {'cm_return':1, 'cm_error':'"cm_action" is not set'}
03700 
03701     # Check again detached console (if disabled and we use php)
03702     if i.get('cm_detach_console','')=='yes' and ini['dcfg'].get('forbid_detached_console','')=='yes':
03703        r={'cm_return':1, 'cm_error':'Detaching console is explicitly forbidden in the kernel'}
03704        prepare_final_out(r, {'cm_console':i.get('cm_console',''), 'cm_output':i.get('cm_output','')})
03705        return r
03706 
03707     # Expand CID to cm_repo_uoa, cm_module_uoa, cm_data_uoa
03708     if 'cid' in i and i['cid']!='':
03709        rx=cm_kernel.cid2array(i)
03710        if rx['cm_return']>0: return rx
03711 
03712        i.update(rx['cm_array'])
03713        del(i['cid'])
03714 
03715     # Check cm_module_uoa vs cm_run_module_uoa
03716     if ('cm_module_uoa' not in i or i['cm_module_uoa']=='') and \
03717        'cm_run_module_uoa' in i and i['cm_run_module_uoa']!='':
03718        i['cm_module_uoa']=i['cm_run_module_uoa']
03719 
03720     # Check remote repository
03721     if (i.get('cm_repo_uoa','')!=''):
03722        cm_repo_uoa=i['cm_repo_uoa']
03723 
03724        r=cm_kernel.access({'cm_run_module_uoa':ini['dcfg']['cm_repo_module_uoa'],
03725                            'cm_action':'find_path_to_repository',
03726                            'find_repo_uoa':cm_repo_uoa})
03727        if r['cm_return']>0: return r
03728 
03729        d=r['cm_data_obj']['cfg']
03730        repo_uoa=r['cm_uoa']
03731        repo_uid=r['cm_uid']
03732 
03733        if 'path_type' in d and d['path_type']=='remote_url':
03734           cur_console=''
03735           if 'cm_console' in i and i['cm_console']!='': cur_console=i['cm_console']
03736 
03737           # Prepare remote request
03738           if i.get('cm_remote_url','')=='': i['cm_remote_url']=d['url']
03739 
03740           if 'cm_run_module_uoa' in i and i['cm_run_module_uoa']!='':
03741              i['cm_web_module_uoa']=i['cm_run_module_uoa']
03742              del(i['cm_run_module_uoa'])
03743           elif 'cm_module_uoa' in i and i['cm_module_uoa']!='':
03744              i['cm_web_module_uoa']=i['cm_module_uoa']
03745              del(i['cm_module_uoa'])
03746           if 'cm_action' in i: 
03747              i['cm_web_action']=i['cm_action']
03748              del(i['cm_action'])
03749           if 'cm_repo_uoa' in i: del(i['cm_repo_uoa'])
03750 
03751           i['cm_console']='json'
03752 
03753           username=''
03754           password=''
03755           password1=''
03756 
03757           # Check if needed remote authentication
03758           if d.get('remote_auth', '')=='yes':
03759              x1='\nRemote repository "'+repo_uoa+'" requires authentication!'
03760              x2=True
03761 
03762              if d.get('remote_auth_as_user', '')=='yes':
03763                 username=ini['web_auth'].get('cm_user_cfg',{}).get('cm_username','')
03764 
03765              if username=='' and d.get('remote_auth_user', '')!='':
03766                 username=d['remote_auth_user']
03767 
03768              if username=='' and ini['dcfg'].get('cm_default_user_uoa', '')!='':
03769                 username=ini['dcfg']['cm_default_user_uoa']
03770 
03771              if env_cm_user_uoa in os.environ.keys() and os.environ[env_cm_user_uoa]!='':
03772                 username=os.environ[env_cm_user_uoa].strip()
03773 
03774              if i.get('cm_remote_user_uoa','')!='':
03775                 username=i['cm_remote_user_uoa']
03776 
03777              if username=='':
03778                 if i.get('cm_web','')=='yes': 
03779                    return {'cm_return':1, 'cm_error':'remote authentication is required but "cm_remote_user_uoa" is not set!'}
03780                 else:
03781                    if cm_repo_uoa in ini['remote_repo'] and ini['remote_repo'][cm_repo_uoa].get('username','')!='':
03782                       username=ini['remote_repo'][cm_repo_uoa]['username']
03783                    else:
03784                       if x2:
03785                          print_for_con(x1)
03786                          x2=False
03787                       username=raw_input('Enter username (or Enter to terminate operation): ')
03788                       if username=='': return {'cm_return':1, 'cm_error':'user terminated operation'}
03789                       if cm_repo_uoa not in ini['remote_repo']: ini['remote_repo'][cm_repo_uoa]={}
03790                       ini['remote_repo'][cm_repo_uoa]['username']=username
03791 
03792              if i.get('cm_remote_user_password','')!='':
03793                 password=i['cm_remote_user_password']
03794              elif env_cm_user_password in os.environ.keys() and os.environ[env_cm_user_password]!='':
03795                 password=os.environ[env_cm_user_password].strip()
03796              elif env_cm_user_password1 in os.environ.keys() and os.environ[env_cm_user_password1]!='':
03797                 password1=os.environ[env_cm_user_password1].strip()
03798              else:
03799                 if i.get('cm_web','')=='yes': return {'cm_return':1, 'cm_error':'remote authentication is required but "cm_remote_user_password" is not set!'}
03800                 else:
03801                    if cm_repo_uoa in ini['remote_repo'] and ini['remote_repo'][cm_repo_uoa].get('password','')!='':
03802                       password=ini['remote_repo'][cm_repo_uoa]['password']
03803                    else:
03804                       if x2:
03805                          print_for_con(x1)
03806                          x2=False
03807                       password=getpass.getpass('Enter password for '+str(username)+' (or Enter to terminate operation): ')
03808                       password=password.strip()
03809                       if password=='': return {'cm_return':1, 'cm_error':'user terminated operation'}
03810                       if cm_repo_uoa not in ini['remote_repo']: ini['remote_repo'][cm_repo_uoa]={}
03811                       ini['remote_repo'][cm_repo_uoa]['password']=password
03812 
03813           rep=''
03814           if i.get('remote_repo_uoa','')!='':
03815              rep=i['remote_repo_uoa']
03816           elif d.get('remote_repo_uoa','')!='':
03817              rep=d['remote_repo_uoa']
03818           if rep!='': i['remote_repo_uoa']=rep
03819 
03820           i['cm_user_uoa']=username
03821 
03822           if password!='': 
03823              i['cm_user_password']=password
03824           if password1!='': 
03825              i['cm_user_password1']=password1
03826 
03827           # Check if needed remote pre-authentication
03828           if d.get('remote_pre_auth', '')=='yes':
03829              i['remote_pre_auth']='yes'
03830 
03831              x1='Remote repository requires http pre-authentication!'
03832              x2=True
03833 
03834              if username=='':
03835                 if d.get('remote_auth_as_user', '')=='yes':
03836                    username=ini['web_auth'].get('cm_user_cfg',{}).get('cm_username','')
03837                 elif d.get('remote_auth_user', '')!='':
03838                    username=d['remote_auth_user']
03839                 elif i.get('cm_remote_user_uoa','')!='':
03840                    username=i['cm_remote_user_uoa']
03841                 else:
03842                    if i.get('cm_web','')=='yes': 
03843                       return {'cm_return':1, 'cm_error':'remote authentication is required but "cm_remote_user_uoa" is not set!'}
03844                    else:
03845                       if x2:
03846                          print_for_con(x1)
03847                          x2=False
03848                       username=raw_input('Enter username (or Enter to terminate operation): ')
03849                       if username=='': return {'cm_return':1, 'cm_error':'user terminated operation'}
03850 
03851              if password=='':
03852                 if i.get('cm_remote_user_password','')!='':
03853                    password=i['cm_remote_user_password']
03854                 else:
03855                    if i.get('cm_web','')=='yes': return {'cm_return':1, 'cm_error':'remote authentication is required but "cm_remote_password" is not set!'}
03856                    else:
03857                       if x2:
03858                          print_for_con(x1)
03859                          x2=False
03860                       password=getpass.getpass('Enter password for '+str(username)+' (or Enter to terminate operation): ')
03861                       if password=='': return {'cm_return':1, 'cm_error':'user terminated operation'}
03862 
03863              i['cm_remote_user_name']=username
03864              i['cm_remote_user_password']=password
03865 
03866           # Send remote request
03867           r=remote_access(i)
03868 
03869           # restore console
03870           if cur_console!='': i['cm_console']=cur_console
03871 
03872           return r
03873 
03874     # Find function in original module and if not found, try common functions in "core" module
03875     for q in range(0,2):
03876 
03877         if q==0:
03878            module=i['cm_run_module_uoa']
03879            actions='cm_actions'
03880         else:
03881            module='core' # hardwired here
03882            actions='cm_common_actions'
03883 
03884         # Load module
03885         ii={}
03886         ii['cm_module_uoa']=module
03887         if 'cm_web' in i: ii['cm_web']=i['cm_web']
03888         r=load_module(ii)
03889         if r['cm_return']>0: 
03890            return r
03891         c=r['cm_code']
03892         e=r.get('external_module','')
03893 
03894         # Check if external or action exists in this module
03895         if e=='yes':
03896            a=getattr(c, i['cm_action'])
03897            return a(i)
03898 
03899         elif actions in c.ini['cfg']:
03900            r=find_index_in_array({
03901                                   'cm_array':c.ini['cfg'][actions],
03902                                   'cm_index':i['cm_action']
03903                                  })
03904 
03905            if r['cm_return']==0:
03906               #Check if skip web pre-processing
03907               if i.get('cm_web_preprocess','')=='yes' \
03908                  and r['cm_array'].get('cm_web_preprocess','')!='yes':
03909                  return {'cm_return':0, 'cm_web_postprocess':'yes'}
03910 
03911               f=r['cm_array']['func']; a=getattr(c, f)
03912               ii={}
03913               if q!=0: ii['cm_common_func']='yes'
03914               if 'params_default' in r['cm_array']:
03915                  merge_arrays({'cm_array':ii, 'cm_array1':r['cm_array']['params_default']})
03916               merge_arrays({'cm_array':ii, 'cm_array1':i})
03917 
03918               if 'cm_web' in i: ii['cm_web']=i['cm_web']
03919 
03920               return a(ii)
03921 
03922     return {'cm_return':1, 'cm_error':'action "'+i['cm_action']+'" not found in module '+i['cm_run_module_uoa']}
03923 
03924 # ============================================================================
03925 if __name__ == "__main__":
03926 
03927    '''
03928    cM command line wrapper
03929    cm <cm object> <action> parameters 
03930 
03931    '''
03932 
03933    r=access_fe_from_cmd(sys.argv[1:])
03934    exit(r['cm_return'])

Generated on Wed May 28 02:49:02 2014 for Collective Mind Framework by DoxyGen 1.6.1
Concept, design and coordination: Grigori Fursin (C) 1993-2013