sprinkle in some async/await
This commit is contained in:
		
							parent
							
								
									297b932db2
								
							
						
					
					
						commit
						71746ca759
					
				
							
								
								
									
										372
									
								
								greenlock.js
									
									
									
									
									
								
							
							
						
						
									
										372
									
								
								greenlock.js
									
									
									
									
									
								
							@ -113,17 +113,16 @@ G.create = function(gconf) {
 | 
				
			|||||||
                request: request
 | 
					                request: request
 | 
				
			||||||
                //punycode: require('punycode')
 | 
					                //punycode: require('punycode')
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .then(function() {
 | 
					            .then(async function() {
 | 
				
			||||||
                return greenlock.manager._defaults().then(function(MCONF) {
 | 
					                var MCONF = await greenlock.manager._defaults();
 | 
				
			||||||
                    mergeDefaults(MCONF, gconf);
 | 
					                mergeDefaults(MCONF, gconf);
 | 
				
			||||||
                    if (true === MCONF.agreeToTerms) {
 | 
					                if (true === MCONF.agreeToTerms) {
 | 
				
			||||||
                        gdefaults.agreeToTerms = function(tos) {
 | 
					                    gdefaults.agreeToTerms = function(tos) {
 | 
				
			||||||
                            return Promise.resolve(tos);
 | 
					                        return Promise.resolve(tos);
 | 
				
			||||||
                        };
 | 
					                    };
 | 
				
			||||||
                    }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return greenlock.manager._defaults(MCONF);
 | 
					                return greenlock.manager._defaults(MCONF);
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .catch(function(err) {
 | 
					            .catch(function(err) {
 | 
				
			||||||
                if ('load_plugin' !== err.context) {
 | 
					                if ('load_plugin' !== err.context) {
 | 
				
			||||||
@ -196,51 +195,47 @@ G.create = function(gconf) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // certs.get
 | 
					    // certs.get
 | 
				
			||||||
    greenlock.get = function(args) {
 | 
					    greenlock.get = async function(args) {
 | 
				
			||||||
        return greenlock
 | 
					        greenlock._single(args);
 | 
				
			||||||
            ._single(args)
 | 
					        args._includePems = true;
 | 
				
			||||||
            .then(function() {
 | 
					        var results = await greenlock.renew(args);
 | 
				
			||||||
                args._includePems = true;
 | 
					        if (!results || !results.length) {
 | 
				
			||||||
                return greenlock.renew(args);
 | 
					            // TODO throw an error here?
 | 
				
			||||||
            })
 | 
					            return null;
 | 
				
			||||||
            .then(function(results) {
 | 
					        }
 | 
				
			||||||
                if (!results || !results.length) {
 | 
					 | 
				
			||||||
                    // TODO throw an error here?
 | 
					 | 
				
			||||||
                    return null;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // just get the first one
 | 
					        // just get the first one
 | 
				
			||||||
                var result = results[0];
 | 
					        var result = results[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // (there should be only one, ideally)
 | 
					        // (there should be only one, ideally)
 | 
				
			||||||
                if (results.length > 1) {
 | 
					        if (results.length > 1) {
 | 
				
			||||||
                    var err = new Error(
 | 
					            var err = new Error(
 | 
				
			||||||
                        "a search for '" +
 | 
					                "a search for '" +
 | 
				
			||||||
                            args.servername +
 | 
					                    args.servername +
 | 
				
			||||||
                            "' returned multiple certificates"
 | 
					                    "' returned multiple certificates"
 | 
				
			||||||
                    );
 | 
					            );
 | 
				
			||||||
                    err.context = 'duplicate_certs';
 | 
					            err.context = 'duplicate_certs';
 | 
				
			||||||
                    err.servername = args.servername;
 | 
					            err.servername = args.servername;
 | 
				
			||||||
                    err.subjects = results.map(function(r) {
 | 
					            err.subjects = results.map(function(r) {
 | 
				
			||||||
                        return (r.site || {}).subject || 'N/A';
 | 
					                return (r.site || {}).subject || 'N/A';
 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    greenlock._notify('warning', err);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (result.error) {
 | 
					 | 
				
			||||||
                    return Promise.reject(result.error);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // site for plugin options, such as http-01 challenge
 | 
					 | 
				
			||||||
                // pems for the obvious reasons
 | 
					 | 
				
			||||||
                return result;
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            greenlock._notify('warning', err);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (result.error) {
 | 
				
			||||||
 | 
					            return Promise.reject(result.error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // site for plugin options, such as http-01 challenge
 | 
				
			||||||
 | 
					        // pems for the obvious reasons
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    greenlock._single = function(args) {
 | 
					    // TODO remove async here, it doesn't matter
 | 
				
			||||||
 | 
					    greenlock._single = async function(args) {
 | 
				
			||||||
        if ('string' !== typeof args.servername) {
 | 
					        if ('string' !== typeof args.servername) {
 | 
				
			||||||
            return Promise.reject(new Error('no `servername` given'));
 | 
					            throw new Error('no `servername` given');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // www.example.com => *.example.com
 | 
					        // www.example.com => *.example.com
 | 
				
			||||||
        args.wildname =
 | 
					        args.wildname =
 | 
				
			||||||
@ -262,116 +257,107 @@ G.create = function(gconf) {
 | 
				
			|||||||
            args.issueBefore ||
 | 
					            args.issueBefore ||
 | 
				
			||||||
            args.expiresBefore
 | 
					            args.expiresBefore
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            return Promise.reject(
 | 
					            throw new Error(
 | 
				
			||||||
                new Error(
 | 
					                'bad arguments, did you mean to call greenlock.renew()?'
 | 
				
			||||||
                    'bad arguments, did you mean to call greenlock.renew()?'
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // duplicate, force, and others still allowed
 | 
					        // duplicate, force, and others still allowed
 | 
				
			||||||
        return Promise.resolve(args);
 | 
					        return args;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    greenlock._config = function(args) {
 | 
					    greenlock._config = async function(args) {
 | 
				
			||||||
        return greenlock._single(args).then(function() {
 | 
					        greenlock._single(args);
 | 
				
			||||||
            return greenlock._configAll(args).then(function(sites) {
 | 
					        var sites = await greenlock._configAll(args);
 | 
				
			||||||
                return sites[0];
 | 
					        return sites[0];
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    greenlock._configAll = function(args) {
 | 
					    greenlock._configAll = async function(args) {
 | 
				
			||||||
        return greenlock._find(args).then(function(sites) {
 | 
					        var sites = await greenlock._find(args);
 | 
				
			||||||
            if (!sites || !sites.length) {
 | 
					        if (!sites || !sites.length) {
 | 
				
			||||||
                return [];
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        sites = JSON.parse(JSON.stringify(sites));
 | 
				
			||||||
 | 
					        var mconf = await greenlock.manager._defaults();
 | 
				
			||||||
 | 
					        return sites.map(function(site) {
 | 
				
			||||||
 | 
					            if (site.store && site.challenges) {
 | 
				
			||||||
 | 
					                return site;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            sites = JSON.parse(JSON.stringify(sites));
 | 
					
 | 
				
			||||||
            return greenlock.manager._defaults().then(function(mconf) {
 | 
					            var dconf = site;
 | 
				
			||||||
                return sites.map(function(site) {
 | 
					            // TODO make cli and api mode the same
 | 
				
			||||||
                    if (site.store && site.challenges) {
 | 
					            if (gconf._bin_mode) {
 | 
				
			||||||
                        return site;
 | 
					                dconf = site.defaults = {};
 | 
				
			||||||
                    }
 | 
					            }
 | 
				
			||||||
                    var dconf = site;
 | 
					            if (!site.store) {
 | 
				
			||||||
                    // TODO make cli and api mode the same
 | 
					                dconf.store = mconf.store;
 | 
				
			||||||
                    if (gconf._bin_mode) {
 | 
					            }
 | 
				
			||||||
                        dconf = site.defaults = {};
 | 
					            if (!site.challenges) {
 | 
				
			||||||
                    }
 | 
					                dconf.challenges = mconf.challenges;
 | 
				
			||||||
                    if (!site.store) {
 | 
					            }
 | 
				
			||||||
                        dconf.store = mconf.store;
 | 
					            return site;
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (!site.challenges) {
 | 
					 | 
				
			||||||
                        dconf.challenges = mconf.challenges;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    return site;
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // needs to get info about the renewal, such as which store and challenge(s) to use
 | 
					    // needs to get info about the renewal, such as which store and challenge(s) to use
 | 
				
			||||||
    greenlock.renew = function(args) {
 | 
					    greenlock.renew = async function(args) {
 | 
				
			||||||
        return greenlock._init().then(function() {
 | 
					        await greenlock._init();
 | 
				
			||||||
            return greenlock.manager._defaults().then(function(mconf) {
 | 
					        var mconf = await greenlock.manager._defaults();
 | 
				
			||||||
                return greenlock._renew(mconf, args);
 | 
					        return greenlock._renew(mconf, args);
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    greenlock._renew = function(mconf, args) {
 | 
					    greenlock._renew = async function(mconf, args) {
 | 
				
			||||||
        if (!args) {
 | 
					        if (!args) {
 | 
				
			||||||
            args = {};
 | 
					            args = {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var renewedOrFailed = [];
 | 
					        var renewedOrFailed = [];
 | 
				
			||||||
        //console.log('greenlock._renew find', args);
 | 
					        //console.log('greenlock._renew find', args);
 | 
				
			||||||
        return greenlock._find(args).then(function(sites) {
 | 
					        var sites = await greenlock._find(args);
 | 
				
			||||||
            // Note: the manager must guaranteed that these are mutable copies
 | 
					        // Note: the manager must guaranteed that these are mutable copies
 | 
				
			||||||
            //console.log('greenlock._renew found', sites);;
 | 
					        //console.log('greenlock._renew found', sites);;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!Array.isArray(sites)) {
 | 
					        if (!Array.isArray(sites)) {
 | 
				
			||||||
                throw new Error(
 | 
					            throw new Error(
 | 
				
			||||||
                    'Developer Error: not an array of sites returned from find: ' +
 | 
					                'Developer Error: not an array of sites returned from find: ' +
 | 
				
			||||||
                        JSON.stringify(sites)
 | 
					                    JSON.stringify(sites)
 | 
				
			||||||
                );
 | 
					            );
 | 
				
			||||||
            }
 | 
					        }
 | 
				
			||||||
            function next() {
 | 
					 | 
				
			||||||
                var site = sites.shift();
 | 
					 | 
				
			||||||
                if (!site) {
 | 
					 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var order = { site: site };
 | 
					        await (async function next() {
 | 
				
			||||||
                renewedOrFailed.push(order);
 | 
					            var site = sites.shift();
 | 
				
			||||||
                // TODO merge args + result?
 | 
					            if (!site) {
 | 
				
			||||||
                return greenlock
 | 
					                return null;
 | 
				
			||||||
                    ._order(mconf, site)
 | 
					 | 
				
			||||||
                    .then(function(pems) {
 | 
					 | 
				
			||||||
                        if (args._includePems) {
 | 
					 | 
				
			||||||
                            order.pems = pems;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                    .catch(function(err) {
 | 
					 | 
				
			||||||
                        order.error = err;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        // For greenlock express serialization
 | 
					 | 
				
			||||||
                        err.toJSON = errorToJSON;
 | 
					 | 
				
			||||||
                        err.context = err.context || 'cert_order';
 | 
					 | 
				
			||||||
                        err.subject = site.subject;
 | 
					 | 
				
			||||||
                        if (args.servername) {
 | 
					 | 
				
			||||||
                            err.servername = args.servername;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        // for debugging, but not to be relied on
 | 
					 | 
				
			||||||
                        err._site = site;
 | 
					 | 
				
			||||||
                        // TODO err.context = err.context || 'renew_certificate'
 | 
					 | 
				
			||||||
                        greenlock._notify('error', err);
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                    .then(function() {
 | 
					 | 
				
			||||||
                        return next();
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return next().then(function() {
 | 
					            var order = { site: site };
 | 
				
			||||||
                return renewedOrFailed;
 | 
					            renewedOrFailed.push(order);
 | 
				
			||||||
            });
 | 
					            // TODO merge args + result?
 | 
				
			||||||
        });
 | 
					            return greenlock
 | 
				
			||||||
 | 
					                ._order(mconf, site)
 | 
				
			||||||
 | 
					                .then(function(pems) {
 | 
				
			||||||
 | 
					                    if (args._includePems) {
 | 
				
			||||||
 | 
					                        order.pems = pems;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .catch(function(err) {
 | 
				
			||||||
 | 
					                    order.error = err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // For greenlock express serialization
 | 
				
			||||||
 | 
					                    err.toJSON = errorToJSON;
 | 
				
			||||||
 | 
					                    err.context = err.context || 'cert_order';
 | 
				
			||||||
 | 
					                    err.subject = site.subject;
 | 
				
			||||||
 | 
					                    if (args.servername) {
 | 
				
			||||||
 | 
					                        err.servername = args.servername;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    // for debugging, but not to be relied on
 | 
				
			||||||
 | 
					                    err._site = site;
 | 
				
			||||||
 | 
					                    // TODO err.context = err.context || 'renew_certificate'
 | 
				
			||||||
 | 
					                    greenlock._notify('error', err);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .then(function() {
 | 
				
			||||||
 | 
					                    return next();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					        })();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return renewedOrFailed;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    greenlock._acme = async function(mconf, args) {
 | 
					    greenlock._acme = async function(mconf, args) {
 | 
				
			||||||
@ -412,70 +398,62 @@ G.create = function(gconf) {
 | 
				
			|||||||
        return acme;
 | 
					        return acme;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    greenlock.order = function(siteConf) {
 | 
					    greenlock.order = async function(siteConf) {
 | 
				
			||||||
        return greenlock._init().then(function() {
 | 
					        await greenlock._init();
 | 
				
			||||||
            return greenlock.manager._defaults().then(function(mconf) {
 | 
					        var mconf = await greenlock.manager._defaults();
 | 
				
			||||||
                return greenlock._order(mconf, siteConf);
 | 
					        return greenlock._order(mconf, siteConf);
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    greenlock._order = function(mconf, siteConf) {
 | 
					    greenlock._order = async function(mconf, siteConf) {
 | 
				
			||||||
        // packageAgent, maintainerEmail
 | 
					        // packageAgent, maintainerEmail
 | 
				
			||||||
        return greenlock._acme(mconf, siteConf).then(function(acme) {
 | 
					        var acme = await greenlock._acme(mconf, siteConf);
 | 
				
			||||||
            var storeConf = siteConf.store || mconf.store;
 | 
					        var storeConf = siteConf.store || mconf.store;
 | 
				
			||||||
            storeConf = JSON.parse(JSON.stringify(storeConf));
 | 
					        storeConf = JSON.parse(JSON.stringify(storeConf));
 | 
				
			||||||
            storeConf.packageRoot = gconf.packageRoot;
 | 
					        storeConf.packageRoot = gconf.packageRoot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var path = require('path');
 | 
					        var path = require('path');
 | 
				
			||||||
            if (!storeConf.basePath) {
 | 
					        if (!storeConf.basePath) {
 | 
				
			||||||
                storeConf.basePath = 'greenlock';
 | 
					            storeConf.basePath = 'greenlock';
 | 
				
			||||||
            }
 | 
					        }
 | 
				
			||||||
            storeConf.basePath = path.resolve(
 | 
					        storeConf.basePath = path.resolve(
 | 
				
			||||||
                gconf.packageRoot || process.cwd(),
 | 
					            gconf.packageRoot || process.cwd(),
 | 
				
			||||||
                storeConf.basePath
 | 
					            storeConf.basePath
 | 
				
			||||||
            );
 | 
					        );
 | 
				
			||||||
            return P._loadStore(storeConf).then(function(store) {
 | 
					        var store = await P._loadStore(storeConf);
 | 
				
			||||||
                return A._getOrCreate(
 | 
					        var account = await A._getOrCreate(
 | 
				
			||||||
                    greenlock,
 | 
					            greenlock,
 | 
				
			||||||
                    mconf,
 | 
					            mconf,
 | 
				
			||||||
                    store.accounts,
 | 
					            store.accounts,
 | 
				
			||||||
                    acme,
 | 
					            acme,
 | 
				
			||||||
                    siteConf
 | 
					            siteConf
 | 
				
			||||||
                ).then(function(account) {
 | 
					        );
 | 
				
			||||||
                    var challengeConfs =
 | 
					        var challengeConfs = siteConf.challenges || mconf.challenges;
 | 
				
			||||||
                        siteConf.challenges || mconf.challenges;
 | 
					        var challenges = {};
 | 
				
			||||||
                    return Promise.all(
 | 
					        var arr = await Promise.all(
 | 
				
			||||||
                        Object.keys(challengeConfs).map(function(typ01) {
 | 
					            Object.keys(challengeConfs).map(function(typ01) {
 | 
				
			||||||
                            return P._loadChallenge(challengeConfs, typ01);
 | 
					                return P._loadChallenge(challengeConfs, typ01);
 | 
				
			||||||
                        })
 | 
					            })
 | 
				
			||||||
                    ).then(function(arr) {
 | 
					        );
 | 
				
			||||||
                        var challenges = {};
 | 
					        arr.forEach(function(el) {
 | 
				
			||||||
                        arr.forEach(function(el) {
 | 
					            challenges[el._type] = el;
 | 
				
			||||||
                            challenges[el._type] = el;
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                        return C._getOrOrder(
 | 
					 | 
				
			||||||
                            greenlock,
 | 
					 | 
				
			||||||
                            mconf,
 | 
					 | 
				
			||||||
                            store.certificates,
 | 
					 | 
				
			||||||
                            acme,
 | 
					 | 
				
			||||||
                            challenges,
 | 
					 | 
				
			||||||
                            account,
 | 
					 | 
				
			||||||
                            siteConf
 | 
					 | 
				
			||||||
                        ).then(function(pems) {
 | 
					 | 
				
			||||||
                            if (!pems) {
 | 
					 | 
				
			||||||
                                throw new Error('no order result');
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            if (!pems.privkey) {
 | 
					 | 
				
			||||||
                                throw new Error(
 | 
					 | 
				
			||||||
                                    'missing private key, which is kinda important'
 | 
					 | 
				
			||||||
                                );
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            return pems;
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var pems = await C._getOrOrder(
 | 
				
			||||||
 | 
					            greenlock,
 | 
				
			||||||
 | 
					            mconf,
 | 
				
			||||||
 | 
					            store.certificates,
 | 
				
			||||||
 | 
					            acme,
 | 
				
			||||||
 | 
					            challenges,
 | 
				
			||||||
 | 
					            account,
 | 
				
			||||||
 | 
					            siteConf
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if (!pems) {
 | 
				
			||||||
 | 
					            throw new Error('no order result');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!pems.privkey) {
 | 
				
			||||||
 | 
					            throw new Error('missing private key, which is kinda important');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return pems;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    greenlock._create();
 | 
					    greenlock._create();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user