2014-02-03 19:24:49 +00:00
|
|
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
2013-06-12 15:59:58 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
require 'resolv'
|
|
|
|
|
2012-04-10 19:57:18 +00:00
|
|
|
class GettingStartedController < ApplicationController
|
|
|
|
|
2012-09-20 12:08:02 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
Resource:
|
2014-11-16 22:45:57 +00:00
|
|
|
GET /api/v1/getting_started
|
2012-09-20 12:08:02 +00:00
|
|
|
|
|
|
|
Response:
|
|
|
|
{
|
|
|
|
"master_user": 1,
|
|
|
|
"groups": [
|
|
|
|
{
|
|
|
|
"name": "group1",
|
|
|
|
"active":true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "group2",
|
|
|
|
"active":true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
Test:
|
2014-11-16 22:45:57 +00:00
|
|
|
curl http://localhost/api/v1/getting_started -v -u #{login}:#{password}
|
2013-06-12 15:59:58 +00:00
|
|
|
|
2012-09-20 12:08:02 +00:00
|
|
|
=end
|
|
|
|
|
2012-04-10 19:57:18 +00:00
|
|
|
def index
|
|
|
|
|
|
|
|
# check if first user already exists
|
2014-10-22 21:00:11 +00:00
|
|
|
return if setup_done_response
|
2012-04-10 19:57:18 +00:00
|
|
|
|
2015-04-08 13:55:53 +00:00
|
|
|
# check it auto wizard is already done
|
2015-04-08 14:07:51 +00:00
|
|
|
auto_wizard_admin = AutoWizard.setup
|
|
|
|
if auto_wizard_admin
|
2015-04-08 13:55:53 +00:00
|
|
|
|
|
|
|
# set current session user
|
2015-04-08 14:07:51 +00:00
|
|
|
current_user_set(auto_wizard_admin)
|
2015-04-08 13:55:53 +00:00
|
|
|
|
|
|
|
# set system init to done
|
|
|
|
Setting.set( 'system_init_done', true )
|
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
auto_wizard: true,
|
|
|
|
setup_done: setup_done,
|
|
|
|
import_mode: Setting.get('import_mode'),
|
|
|
|
import_backend: Setting.get('import_backend'),
|
|
|
|
system_online_service: Setting.get('system_online_service'),
|
2015-04-08 13:55:53 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2012-09-20 12:08:02 +00:00
|
|
|
# if master user already exists, we need to be authenticated
|
2014-10-22 21:00:11 +00:00
|
|
|
if setup_done
|
2012-09-20 12:08:02 +00:00
|
|
|
return if !authentication_check
|
|
|
|
end
|
|
|
|
|
2012-04-10 19:57:18 +00:00
|
|
|
# return result
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
setup_done: setup_done,
|
|
|
|
import_mode: Setting.get('import_mode'),
|
|
|
|
import_backend: Setting.get('import_backend'),
|
|
|
|
system_online_service: Setting.get('system_online_service'),
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
def base
|
|
|
|
|
|
|
|
# check admin permissions
|
2015-02-15 09:12:27 +00:00
|
|
|
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# validate url
|
|
|
|
messages = {}
|
2014-11-20 16:04:31 +00:00
|
|
|
if !Setting.get('system_online_service')
|
2015-05-01 12:12:37 +00:00
|
|
|
if !params[:url] || params[:url] !~ %r{^(http|https)://.+?$}
|
2014-11-20 16:04:31 +00:00
|
|
|
messages[:url] = 'A URL looks like http://zammad.example.com'
|
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# validate organization
|
|
|
|
if !params[:organization] || params[:organization].empty?
|
|
|
|
messages[:organization] = 'Invalid!'
|
|
|
|
end
|
|
|
|
|
|
|
|
# validate image
|
2014-11-19 22:45:37 +00:00
|
|
|
if params[:logo] && params[:logo] =~ /^data:image/i
|
2014-11-16 22:45:57 +00:00
|
|
|
|
2014-11-19 22:45:37 +00:00
|
|
|
file = StaticAssets.data_url_attributes( params[:logo] )
|
2014-11-16 22:45:57 +00:00
|
|
|
|
2014-12-01 07:32:35 +00:00
|
|
|
if !file[:content] || !file[:mime_type]
|
2014-11-16 22:45:57 +00:00
|
|
|
messages[:logo] = 'Unable to process image upload.'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if !messages.empty?
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'invalid',
|
|
|
|
messages: messages,
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
2014-10-22 21:26:00 +00:00
|
|
|
return
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
|
|
|
|
2014-10-22 21:26:00 +00:00
|
|
|
# split url in http_type and fqdn
|
2014-11-16 22:45:57 +00:00
|
|
|
settings = {}
|
2014-11-20 16:04:31 +00:00
|
|
|
if !Setting.get('system_online_service')
|
2015-05-01 12:12:37 +00:00
|
|
|
if params[:url] =~ %r{/^(http|https)://(.+?)$}
|
2014-11-20 16:04:31 +00:00
|
|
|
Setting.set('http_type', $1)
|
|
|
|
settings[:http_type] = $1
|
|
|
|
Setting.set('fqdn', $2)
|
|
|
|
settings[:fqdn] = $2
|
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
2014-10-22 21:26:00 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# save organization
|
|
|
|
Setting.set('organization', params[:organization])
|
|
|
|
settings[:organization] = params[:organization]
|
|
|
|
|
|
|
|
# save image
|
2014-11-19 22:45:37 +00:00
|
|
|
if params[:logo] && params[:logo] =~ /^data:image/i
|
2014-11-16 22:45:57 +00:00
|
|
|
|
|
|
|
# data:image/png;base64
|
2014-11-19 22:22:13 +00:00
|
|
|
file = StaticAssets.data_url_attributes( params[:logo] )
|
|
|
|
|
|
|
|
# store image 1:1
|
2014-12-01 07:32:35 +00:00
|
|
|
StaticAssets.store_raw( file[:content], file[:mime_type] )
|
2014-10-22 21:26:00 +00:00
|
|
|
end
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2014-11-19 22:45:37 +00:00
|
|
|
if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i
|
2014-11-19 22:22:13 +00:00
|
|
|
|
|
|
|
# data:image/png;base64
|
|
|
|
file = StaticAssets.data_url_attributes( params[:logo_resize] )
|
|
|
|
|
|
|
|
# store image 1:1
|
2014-12-01 07:32:35 +00:00
|
|
|
settings[:product_logo] = StaticAssets.store( file[:content], file[:mime_type] )
|
2014-11-19 22:22:13 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# set changed settings
|
|
|
|
settings.each {|key, value|
|
|
|
|
Setting.set(key, value)
|
|
|
|
}
|
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'ok',
|
|
|
|
settings: settings,
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
def email_probe
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# check admin permissions
|
2015-02-15 09:12:27 +00:00
|
|
|
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
2014-11-16 22:45:57 +00:00
|
|
|
|
|
|
|
# validation
|
|
|
|
user = nil
|
|
|
|
domain = nil
|
|
|
|
if params[:email] =~ /^(.+?)@(.+?)$/
|
|
|
|
user = $1
|
|
|
|
domain = $2
|
|
|
|
end
|
|
|
|
|
|
|
|
if !user || !domain
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'invalid',
|
|
|
|
messages: {
|
|
|
|
email: 'Invalid email.'
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# check domain based attributes
|
2015-04-27 20:49:17 +00:00
|
|
|
provider_map = {
|
2015-04-27 13:42:53 +00:00
|
|
|
google: {
|
|
|
|
domain: 'gmail.com|googlemail.com|gmail.de',
|
|
|
|
inbound: {
|
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: 'imap.gmail.com',
|
|
|
|
port: '993',
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-10-22 21:00:11 +00:00
|
|
|
},
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
2015-04-27 13:42:53 +00:00
|
|
|
outbound: {
|
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: 'smtp.gmail.com',
|
|
|
|
port: '25',
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-12-11 10:44:23 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2015-04-27 13:42:53 +00:00
|
|
|
microsoft: {
|
|
|
|
domain: 'outlook.com|hotmail.com',
|
|
|
|
inbound: {
|
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: 'imap-mail.outlook.com',
|
|
|
|
port: '993',
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-12-11 10:44:23 +00:00
|
|
|
},
|
|
|
|
},
|
2015-04-27 13:42:53 +00:00
|
|
|
outbound: {
|
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: 'smtp-mail.outlook.com',
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# probe based on email domain and mx
|
|
|
|
domains = [domain]
|
|
|
|
mail_exchangers = mxers(domain)
|
|
|
|
if mail_exchangers && mail_exchangers[0]
|
2014-11-18 21:09:01 +00:00
|
|
|
logger.info "MX for #{domain}: #{mail_exchangers} - #{mail_exchangers[0][0]}"
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0]
|
|
|
|
domains.push mail_exchangers[0][0]
|
|
|
|
end
|
2015-04-27 20:49:17 +00:00
|
|
|
provider_map.each {|provider, settings|
|
2014-11-16 22:45:57 +00:00
|
|
|
domains.each {|domain_to_check|
|
|
|
|
if domain_to_check =~ /#{settings[:domain]}/i
|
|
|
|
|
|
|
|
# probe inbound
|
|
|
|
result = email_probe_inbound( settings[:inbound] )
|
2014-11-17 10:35:36 +00:00
|
|
|
if result[:result] != 'ok'
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: result
|
2014-10-22 21:00:11 +00:00
|
|
|
return
|
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
|
|
|
|
# probe outbound
|
|
|
|
result = email_probe_outbound( settings[:outbound], params[:email] )
|
|
|
|
if result[:result] != 'ok'
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: result
|
2014-11-16 22:45:57 +00:00
|
|
|
return
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'ok',
|
|
|
|
setting: settings,
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# probe inbound
|
2015-04-27 20:49:17 +00:00
|
|
|
inbound_map = []
|
2014-11-16 22:45:57 +00:00
|
|
|
if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0]
|
2015-04-27 20:49:17 +00:00
|
|
|
inbound_mx = [
|
2014-11-16 22:45:57 +00:00
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: mail_exchangers[0][0],
|
|
|
|
port: 993,
|
|
|
|
ssl: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: mail_exchangers[0][0],
|
|
|
|
port: 993,
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
2015-04-27 20:49:17 +00:00
|
|
|
inbound_map = inbound_map + inbound_mx
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
2015-04-27 20:49:17 +00:00
|
|
|
inbound_auto = [
|
2014-11-16 22:45:57 +00:00
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 993,
|
|
|
|
ssl: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 993,
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: "imap.#{domain}",
|
|
|
|
port: 993,
|
|
|
|
ssl: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: "imap.#{domain}",
|
|
|
|
port: 993,
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'pop3',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 995,
|
|
|
|
ssl: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'pop3',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 995,
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'pop3',
|
|
|
|
options: {
|
|
|
|
host: "pop.#{domain}",
|
|
|
|
port: 995,
|
|
|
|
ssl: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'pop3',
|
|
|
|
options: {
|
|
|
|
host: "pop.#{domain}",
|
|
|
|
port: 995,
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'pop3',
|
|
|
|
options: {
|
|
|
|
host: "pop3.#{domain}",
|
|
|
|
port: 995,
|
|
|
|
ssl: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'pop3',
|
|
|
|
options: {
|
|
|
|
host: "pop3.#{domain}",
|
|
|
|
port: 995,
|
|
|
|
ssl: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
2015-04-27 20:49:17 +00:00
|
|
|
inbound_map = inbound_map + inbound_auto
|
2014-11-16 22:45:57 +00:00
|
|
|
settings = {}
|
|
|
|
success = false
|
2015-04-27 20:49:17 +00:00
|
|
|
inbound_map.each {|config|
|
2014-11-18 21:09:01 +00:00
|
|
|
logger.info "INBOUND PROBE: #{config.inspect}"
|
2014-11-16 22:45:57 +00:00
|
|
|
result = email_probe_inbound( config )
|
2014-11-18 21:09:01 +00:00
|
|
|
logger.info "INBOUND RESULT: #{result.inspect}"
|
2014-11-17 10:35:36 +00:00
|
|
|
if result[:result] == 'ok'
|
2014-11-16 22:45:57 +00:00
|
|
|
success = true
|
|
|
|
settings[:inbound] = config
|
|
|
|
break
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
if !success
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'failed',
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
# probe outbound
|
2015-04-27 20:49:17 +00:00
|
|
|
outbound_map = []
|
2014-11-16 22:45:57 +00:00
|
|
|
if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0]
|
2015-04-27 20:49:17 +00:00
|
|
|
outbound_mx = [
|
2014-11-16 22:45:57 +00:00
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: mail_exchangers[0][0],
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-10-22 21:00:11 +00:00
|
|
|
},
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: mail_exchangers[0][0],
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: mail_exchangers[0][0],
|
|
|
|
port: 465,
|
|
|
|
start_tls: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: mail_exchangers[0][0],
|
|
|
|
port: 465,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
2015-04-27 20:49:17 +00:00
|
|
|
outbound_map = outbound_map + outbound_mx
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
2015-04-27 20:49:17 +00:00
|
|
|
outbound_auto = [
|
2014-11-16 22:45:57 +00:00
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 465,
|
|
|
|
start_tls: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "mail.#{domain}",
|
|
|
|
port: 465,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "smtp.#{domain}",
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "smtp.#{domain}",
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "smtp.#{domain}",
|
|
|
|
port: 465,
|
|
|
|
start_tls: true,
|
|
|
|
user: user,
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: "smtp.#{domain}",
|
|
|
|
port: 465,
|
|
|
|
start_tls: true,
|
|
|
|
user: params[:email],
|
|
|
|
password: params[:password],
|
2014-11-16 22:45:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
success = false
|
2015-04-27 20:49:17 +00:00
|
|
|
outbound_map.each {|config|
|
2014-11-18 21:09:01 +00:00
|
|
|
logger.info "OUTBOUND PROBE: #{config.inspect}"
|
2014-11-16 22:45:57 +00:00
|
|
|
result = email_probe_outbound( config, params[:email] )
|
2014-11-18 21:09:01 +00:00
|
|
|
logger.info "OUTBOUND RESULT: #{result.inspect}"
|
2014-11-16 22:45:57 +00:00
|
|
|
if result[:result] == 'ok'
|
|
|
|
success = true
|
|
|
|
settings[:outbound] = config
|
|
|
|
break
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if !success
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'failed',
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'ok',
|
|
|
|
setting: settings,
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def email_outbound
|
|
|
|
|
|
|
|
# check admin permissions
|
2015-02-15 09:12:27 +00:00
|
|
|
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
2014-11-16 22:45:57 +00:00
|
|
|
|
|
|
|
# validate params
|
|
|
|
if !params[:adapter]
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'invalid',
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
# connection test
|
|
|
|
result = email_probe_outbound( params, params[:email] )
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: result
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
def email_inbound
|
|
|
|
|
|
|
|
# check admin permissions
|
2015-02-15 09:12:27 +00:00
|
|
|
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
2014-10-22 21:00:11 +00:00
|
|
|
|
|
|
|
# validate params
|
|
|
|
if !params[:adapter]
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'invalid',
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
# connection test
|
2014-11-16 22:45:57 +00:00
|
|
|
result = email_probe_inbound( params )
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: result
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def email_verify
|
|
|
|
|
|
|
|
# check admin permissions
|
2015-02-15 09:12:27 +00:00
|
|
|
return if deny_if_not_role(Z_ROLENAME_ADMIN)
|
2014-11-16 22:45:57 +00:00
|
|
|
|
2014-10-22 21:00:11 +00:00
|
|
|
# send verify email to inbox
|
2014-11-18 11:51:35 +00:00
|
|
|
if !params[:subject]
|
2015-04-27 13:43:34 +00:00
|
|
|
subject = '#' + rand(99_999_999_999).to_s
|
2014-11-18 11:51:35 +00:00
|
|
|
else
|
|
|
|
subject = params[:subject]
|
|
|
|
end
|
|
|
|
result = email_probe_outbound( params[:outbound], params[:meta][:email], subject )
|
|
|
|
|
2014-11-18 21:09:01 +00:00
|
|
|
(1..5).each {|loop|
|
|
|
|
sleep 10
|
2014-10-22 21:00:11 +00:00
|
|
|
|
|
|
|
# fetch mailbox
|
|
|
|
found = nil
|
2014-11-18 21:09:01 +00:00
|
|
|
|
|
|
|
begin
|
|
|
|
if params[:inbound][:adapter] =~ /^imap$/i
|
2015-04-27 13:42:53 +00:00
|
|
|
found = Channel::IMAP.new.fetch( { options: params[:inbound][:options] }, 'verify', subject )
|
2014-11-18 21:09:01 +00:00
|
|
|
else
|
2015-04-27 13:42:53 +00:00
|
|
|
found = Channel::POP3.new.fetch( { options: params[:inbound][:options] }, 'verify', subject )
|
2014-11-18 21:09:01 +00:00
|
|
|
end
|
|
|
|
rescue Exception => e
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'invalid',
|
|
|
|
message: e.to_s,
|
|
|
|
subject: subject,
|
2014-11-18 21:09:01 +00:00
|
|
|
}
|
|
|
|
return
|
2014-10-22 21:00:11 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if found && found == 'verify ok'
|
|
|
|
|
|
|
|
# remember address
|
2015-04-27 13:42:53 +00:00
|
|
|
address = EmailAddress.where( email: params[:meta][:email] ).first
|
2014-11-20 16:04:31 +00:00
|
|
|
if !address
|
|
|
|
address = EmailAddress.first
|
|
|
|
end
|
2014-10-22 21:00:11 +00:00
|
|
|
if address
|
|
|
|
address.update_attributes(
|
2015-04-27 13:42:53 +00:00
|
|
|
realname: params[:meta][:realname],
|
|
|
|
email: params[:meta][:email],
|
|
|
|
active: 1,
|
|
|
|
updated_by_id: 1,
|
|
|
|
created_by_id: 1,
|
2014-10-22 21:00:11 +00:00
|
|
|
)
|
|
|
|
else
|
|
|
|
EmailAddress.create(
|
2015-04-27 13:42:53 +00:00
|
|
|
realname: params[:meta][:realname],
|
|
|
|
email: params[:meta][:email],
|
|
|
|
active: 1,
|
|
|
|
updated_by_id: 1,
|
|
|
|
created_by_id: 1,
|
2014-10-22 21:00:11 +00:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
# store mailbox
|
|
|
|
Channel.create(
|
2015-04-27 13:42:53 +00:00
|
|
|
area: 'Email::Inbound',
|
|
|
|
adapter: params[:inbound][:adapter],
|
|
|
|
options: params[:inbound][:options],
|
|
|
|
group_id: 1,
|
|
|
|
active: 1,
|
|
|
|
updated_by_id: 1,
|
|
|
|
created_by_id: 1,
|
2014-10-22 21:00:11 +00:00
|
|
|
)
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# save settings
|
|
|
|
if params[:outbound][:adapter] =~ /^smtp$/i
|
2015-04-27 13:42:53 +00:00
|
|
|
smtp = Channel.where( adapter: 'SMTP', area: 'Email::Outbound' ).first
|
2014-11-16 22:45:57 +00:00
|
|
|
smtp.options = params[:outbound][:options]
|
|
|
|
smtp.active = true
|
|
|
|
smtp.save!
|
2015-04-27 13:42:53 +00:00
|
|
|
sendmail = Channel.where( adapter: 'Sendmail' ).first
|
2014-11-16 22:45:57 +00:00
|
|
|
sendmail.active = false
|
|
|
|
sendmail.save!
|
|
|
|
else
|
2015-04-27 13:42:53 +00:00
|
|
|
sendmail = Channel.where( adapter: 'Sendmail', area: 'Email::Outbound' ).first
|
2014-11-16 22:45:57 +00:00
|
|
|
sendmail.options = {}
|
|
|
|
sendmail.active = true
|
|
|
|
sendmail.save!
|
2015-04-27 13:42:53 +00:00
|
|
|
smtp = Channel.where( adapter: 'SMTP' ).first
|
2014-11-16 22:45:57 +00:00
|
|
|
smtp.active = false
|
|
|
|
smtp.save
|
|
|
|
end
|
|
|
|
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'ok',
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
2014-11-18 21:09:01 +00:00
|
|
|
# check delivery for 30 sek.
|
2015-04-27 13:42:53 +00:00
|
|
|
render json: {
|
|
|
|
result: 'invalid',
|
|
|
|
message: 'Verification Email not found in mailbox.',
|
|
|
|
subject: subject,
|
2014-10-22 21:00:11 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2014-11-18 11:51:35 +00:00
|
|
|
def email_probe_outbound(params, email, subject = nil)
|
2014-11-16 22:45:57 +00:00
|
|
|
|
|
|
|
# validate params
|
|
|
|
if !params[:adapter]
|
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'invalid',
|
|
|
|
message: 'Invalid, need adapter!',
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2014-11-18 11:51:35 +00:00
|
|
|
if subject
|
|
|
|
mail = {
|
|
|
|
:from => email,
|
|
|
|
:to => email,
|
|
|
|
:subject => "Zammad Getting started Test Email #{subject}",
|
2014-12-14 19:18:54 +00:00
|
|
|
:body => "This is a Test Email of Zammad to check if sending and receiving is working correctly.\n\nYou can ignore or delete this email.",
|
2014-11-18 11:51:35 +00:00
|
|
|
'x-zammad-ignore' => 'true',
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mail = {
|
2015-04-27 13:42:53 +00:00
|
|
|
from: email,
|
|
|
|
to: 'emailtrytest@znuny.com',
|
|
|
|
subject: 'test',
|
|
|
|
body: 'test',
|
2014-11-18 11:51:35 +00:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
# test connection
|
2015-04-27 20:49:17 +00:00
|
|
|
translation_map = {
|
2014-11-17 10:35:36 +00:00
|
|
|
'authentication failed' => 'Authentication failed!',
|
2014-11-18 21:09:01 +00:00
|
|
|
'Incorrect username' => 'Authentication failed!',
|
2014-11-16 22:45:57 +00:00
|
|
|
'getaddrinfo: nodename nor servname provided, or not known' => 'Hostname not found!',
|
2014-11-17 10:35:36 +00:00
|
|
|
'No route to host' => 'No route to host!',
|
|
|
|
'Connection refused' => 'Connection refused!',
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
2014-11-18 11:51:35 +00:00
|
|
|
if params[:adapter] =~ /^smtp$/i
|
2014-11-18 21:09:01 +00:00
|
|
|
|
|
|
|
# in case, fill missing params
|
2015-04-27 20:49:17 +00:00
|
|
|
if !params[:options].key?(:port)
|
2014-11-18 21:09:01 +00:00
|
|
|
params[:options][:port] = 25
|
|
|
|
end
|
2015-04-27 20:49:17 +00:00
|
|
|
if !params[:options].key?(:ssl)
|
2014-11-18 21:09:01 +00:00
|
|
|
params[:options][:ssl] = true
|
|
|
|
end
|
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
begin
|
|
|
|
Channel::SMTP.new.send(
|
2014-11-18 11:51:35 +00:00
|
|
|
mail,
|
2014-11-16 22:45:57 +00:00
|
|
|
{
|
2015-04-27 13:42:53 +00:00
|
|
|
options: params[:options]
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
rescue Exception => e
|
|
|
|
|
|
|
|
# check if sending email was ok, but mailserver rejected
|
2014-11-18 11:51:35 +00:00
|
|
|
if !subject
|
2015-04-27 20:49:17 +00:00
|
|
|
white_map = {
|
2014-11-18 11:51:35 +00:00
|
|
|
'Recipient address rejected' => true,
|
|
|
|
}
|
2015-04-27 20:49:17 +00:00
|
|
|
white_map.each {|key, message|
|
2014-11-18 11:51:35 +00:00
|
|
|
if e.message =~ /#{Regexp.escape(key)}/i
|
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'ok',
|
|
|
|
settings: params,
|
|
|
|
notice: e.message,
|
2014-11-18 11:51:35 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
2014-11-16 22:45:57 +00:00
|
|
|
message_human = ''
|
2015-04-27 20:49:17 +00:00
|
|
|
translation_map.each {|key, message|
|
2014-11-16 22:45:57 +00:00
|
|
|
if e.message =~ /#{Regexp.escape(key)}/i
|
|
|
|
message_human = message
|
|
|
|
end
|
|
|
|
}
|
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'invalid',
|
|
|
|
settings: params,
|
|
|
|
message: e.message,
|
|
|
|
message_human: message_human,
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
end
|
2014-11-17 10:35:36 +00:00
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'ok',
|
2014-11-17 10:35:36 +00:00
|
|
|
}
|
|
|
|
return result
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
begin
|
|
|
|
Channel::Sendmail.new.send(
|
2014-11-18 11:51:35 +00:00
|
|
|
mail,
|
2014-11-16 22:45:57 +00:00
|
|
|
nil
|
|
|
|
)
|
|
|
|
rescue Exception => e
|
|
|
|
message_human = ''
|
2015-04-27 20:49:17 +00:00
|
|
|
translation_map.each {|key, message|
|
2014-11-16 22:45:57 +00:00
|
|
|
if e.message =~ /#{Regexp.escape(key)}/i
|
|
|
|
message_human = message
|
|
|
|
end
|
|
|
|
}
|
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'invalid',
|
|
|
|
settings: params,
|
|
|
|
message: e.message,
|
|
|
|
message_human: message_human,
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
end
|
2014-11-17 10:35:36 +00:00
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'ok',
|
2014-11-17 10:35:36 +00:00
|
|
|
}
|
2015-04-27 20:49:17 +00:00
|
|
|
result
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def email_probe_inbound(params)
|
|
|
|
|
|
|
|
# validate params
|
|
|
|
if !params[:adapter]
|
|
|
|
raise 'need :adapter param'
|
|
|
|
end
|
|
|
|
|
|
|
|
# connection test
|
2015-04-27 20:49:17 +00:00
|
|
|
translation_map = {
|
2014-11-16 22:45:57 +00:00
|
|
|
'authentication failed' => 'Authentication failed!',
|
2014-11-18 21:09:01 +00:00
|
|
|
'Incorrect username' => 'Authentication failed!',
|
2014-11-16 22:45:57 +00:00
|
|
|
'getaddrinfo: nodename nor servname provided, or not known' => 'Hostname not found!',
|
|
|
|
'No route to host' => 'No route to host!',
|
|
|
|
'Connection refused' => 'Connection refused!',
|
|
|
|
}
|
|
|
|
if params[:adapter] =~ /^imap$/i
|
2014-11-18 21:09:01 +00:00
|
|
|
|
2014-11-16 22:45:57 +00:00
|
|
|
begin
|
2015-04-27 13:42:53 +00:00
|
|
|
Channel::IMAP.new.fetch( { options: params[:options] }, 'check' )
|
2014-11-16 22:45:57 +00:00
|
|
|
rescue Exception => e
|
|
|
|
message_human = ''
|
2015-04-27 20:49:17 +00:00
|
|
|
translation_map.each {|key, message|
|
2014-11-16 22:45:57 +00:00
|
|
|
if e.message =~ /#{Regexp.escape(key)}/i
|
|
|
|
message_human = message
|
|
|
|
end
|
|
|
|
}
|
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'invalid',
|
|
|
|
settings: params,
|
|
|
|
message: e.message,
|
|
|
|
message_human: message_human,
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
end
|
2014-11-17 10:35:36 +00:00
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'ok',
|
2014-11-17 10:35:36 +00:00
|
|
|
}
|
|
|
|
return result
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
begin
|
2015-04-27 13:42:53 +00:00
|
|
|
Channel::POP3.new.fetch( { options: params[:options] }, 'check' )
|
2014-11-16 22:45:57 +00:00
|
|
|
rescue Exception => e
|
|
|
|
message_human = ''
|
2015-04-27 20:49:17 +00:00
|
|
|
translation_map.each {|key, message|
|
2014-11-16 22:45:57 +00:00
|
|
|
if e.message =~ /#{Regexp.escape(key)}/i
|
|
|
|
message_human = message
|
|
|
|
end
|
|
|
|
}
|
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'invalid',
|
|
|
|
settings: params,
|
|
|
|
message: e.message,
|
|
|
|
message_human: message_human,
|
2014-11-16 22:45:57 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
end
|
2014-11-17 10:35:36 +00:00
|
|
|
result = {
|
2015-04-27 13:42:53 +00:00
|
|
|
result: 'ok',
|
2014-11-17 10:35:36 +00:00
|
|
|
}
|
2015-04-27 20:49:17 +00:00
|
|
|
result
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def mxers(domain)
|
2014-12-09 10:31:50 +00:00
|
|
|
begin
|
|
|
|
mxs = Resolv::DNS.open do |dns|
|
|
|
|
ress = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
|
2015-05-05 14:25:28 +00:00
|
|
|
ress.map { |r| [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference] }
|
2014-12-09 10:31:50 +00:00
|
|
|
end
|
|
|
|
rescue Exception => e
|
|
|
|
logger.error e.message
|
|
|
|
logger.error e.backtrace.inspect
|
2014-11-16 22:45:57 +00:00
|
|
|
end
|
|
|
|
mxs
|
|
|
|
end
|
|
|
|
|
2014-10-22 21:00:11 +00:00
|
|
|
def setup_done
|
2014-11-01 10:57:02 +00:00
|
|
|
#return false
|
2014-10-22 21:00:11 +00:00
|
|
|
count = User.all.count()
|
|
|
|
done = true
|
|
|
|
if count <= 2
|
|
|
|
done = false
|
|
|
|
end
|
|
|
|
done
|
|
|
|
end
|
|
|
|
|
|
|
|
def setup_done_response
|
|
|
|
if !setup_done
|
|
|
|
return false
|
|
|
|
end
|
2014-11-20 16:04:31 +00:00
|
|
|
|
|
|
|
# get all groups
|
2015-04-27 13:42:53 +00:00
|
|
|
groups = Group.where( active: true )
|
2014-11-20 16:04:31 +00:00
|
|
|
|
|
|
|
# get email addresses
|
2015-04-27 13:42:53 +00:00
|
|
|
addresses = EmailAddress.where( active: true )
|
|
|
|
|
|
|
|
render json: {
|
|
|
|
setup_done: true,
|
|
|
|
import_mode: Setting.get('import_mode'),
|
|
|
|
import_backend: Setting.get('import_backend'),
|
|
|
|
system_online_service: Setting.get('system_online_service'),
|
|
|
|
addresses: addresses,
|
|
|
|
groups: groups,
|
2012-04-11 06:34:56 +00:00
|
|
|
}
|
2014-10-22 21:00:11 +00:00
|
|
|
true
|
2012-04-10 19:57:18 +00:00
|
|
|
end
|
2014-10-22 21:00:11 +00:00
|
|
|
|
2015-04-27 14:15:29 +00:00
|
|
|
end
|