zammad/app/controllers/getting_started_controller.rb
2015-05-24 23:59:11 +02:00

984 lines
23 KiB
Ruby

# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
require 'resolv'
class GettingStartedController < ApplicationController
=begin
Resource:
GET /api/v1/getting_started
Response:
{
"master_user": 1,
"groups": [
{
"name": "group1",
"active":true
},
{
"name": "group2",
"active":true
}
]
}
Test:
curl http://localhost/api/v1/getting_started -v -u #{login}:#{password}
=end
def index
# check if first user already exists
return if setup_done_response
# check it auto wizard is already done
return if auto_wizard_enabled_response
# if master user already exists, we need to be authenticated
if setup_done
return if !authentication_check
end
# return result
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'),
}
end
def auto_wizard_admin
# check if system setup is already done
return if setup_done_response
# check it auto wizard is enabled
if !AutoWizard.enabled?
render json: {
auto_wizard: false,
}
return
end
# verify auto wizard file
auto_wizard_data = AutoWizard.data
if !auto_wizard_data || auto_wizard_data.empty?
render json: {
auto_wizard: true,
auto_wizard_success: false,
message: 'Invalid auto wizard file.',
}
return
end
# verify auto wizard token
if auto_wizard_data['Token'] && auto_wizard_data['Token'] != params[:token]
render json: {
auto_wizard: true,
auto_wizard_success: false,
}
return
end
# execute auto wizard
auto_wizard_admin = AutoWizard.setup
if !auto_wizard_admin
render json: {
auto_wizard: true,
auto_wizard_success: false,
message: 'Error during execution of auto wizard.',
}
return
end
# set current session user
current_user_set(auto_wizard_admin)
# set system init to done
Setting.set('system_init_done', true)
render json: {
auto_wizard: true,
auto_wizard_success: true,
}
end
def base
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# validate url
messages = {}
if !Setting.get('system_online_service')
if !params[:url] || params[:url] !~ %r{^(http|https)://.+?$}
messages[:url] = 'A URL looks like http://zammad.example.com'
end
end
# validate organization
if !params[:organization] || params[:organization].empty?
messages[:organization] = 'Invalid!'
end
# validate image
if params[:logo] && params[:logo] =~ /^data:image/i
file = StaticAssets.data_url_attributes( params[:logo] )
if !file[:content] || !file[:mime_type]
messages[:logo] = 'Unable to process image upload.'
end
end
if !messages.empty?
render json: {
result: 'invalid',
messages: messages,
}
return
end
# split url in http_type and fqdn
settings = {}
if !Setting.get('system_online_service')
if params[:url] =~ %r{/^(http|https)://(.+?)$}
Setting.set('http_type', $1)
settings[:http_type] = $1
Setting.set('fqdn', $2)
settings[:fqdn] = $2
end
end
# save organization
Setting.set('organization', params[:organization])
settings[:organization] = params[:organization]
# save image
if params[:logo] && params[:logo] =~ /^data:image/i
# data:image/png;base64
file = StaticAssets.data_url_attributes( params[:logo] )
# store image 1:1
StaticAssets.store_raw( file[:content], file[:mime_type] )
end
if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i
# data:image/png;base64
file = StaticAssets.data_url_attributes( params[:logo_resize] )
# store image 1:1
settings[:product_logo] = StaticAssets.store( file[:content], file[:mime_type] )
end
# set changed settings
settings.each {|key, value|
Setting.set(key, value)
}
render json: {
result: 'ok',
settings: settings,
}
end
def email_probe
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# validation
user = nil
domain = nil
if params[:email] =~ /^(.+?)@(.+?)$/
user = $1
domain = $2
end
if !user || !domain
render json: {
result: 'invalid',
messages: {
email: 'Invalid email.'
},
}
return
end
# check domain based attributes
provider_map = {
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],
},
},
outbound: {
adapter: 'smtp',
options: {
host: 'smtp.gmail.com',
port: '25',
start_tls: true,
user: params[:email],
password: params[:password],
}
},
},
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],
},
},
outbound: {
adapter: 'smtp',
options: {
host: 'smtp-mail.outlook.com',
port: 25,
start_tls: true,
user: params[:email],
password: params[:password],
}
},
},
}
# probe based on email domain and mx
domains = [domain]
mail_exchangers = mxers(domain)
if mail_exchangers && mail_exchangers[0]
logger.info "MX for #{domain}: #{mail_exchangers} - #{mail_exchangers[0][0]}"
end
if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0]
domains.push mail_exchangers[0][0]
end
provider_map.each {|_provider, settings|
domains.each {|domain_to_check|
next if domain_to_check !~ /#{settings[:domain]}/i
# probe inbound
result = email_probe_inbound( settings[:inbound] )
if result[:result] != 'ok'
render json: result
return # rubocop:disable Lint/NonLocalExitFromIterator
end
# probe outbound
result = email_probe_outbound( settings[:outbound], params[:email] )
if result[:result] != 'ok'
render json: result
return # rubocop:disable Lint/NonLocalExitFromIterator
end
render json: {
result: 'ok',
setting: settings,
}
return # rubocop:disable Lint/NonLocalExitFromIterator
}
}
# probe inbound
inbound_map = []
if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0]
inbound_mx = [
{
adapter: 'imap',
options: {
host: mail_exchangers[0][0],
port: 993,
ssl: true,
user: user,
password: params[:password],
},
},
{
adapter: 'imap',
options: {
host: mail_exchangers[0][0],
port: 993,
ssl: true,
user: params[:email],
password: params[:password],
},
},
]
inbound_map = inbound_map + inbound_mx
end
inbound_auto = [
{
adapter: 'imap',
options: {
host: "mail.#{domain}",
port: 993,
ssl: true,
user: user,
password: params[:password],
},
},
{
adapter: 'imap',
options: {
host: "mail.#{domain}",
port: 993,
ssl: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'imap',
options: {
host: "imap.#{domain}",
port: 993,
ssl: true,
user: user,
password: params[:password],
},
},
{
adapter: 'imap',
options: {
host: "imap.#{domain}",
port: 993,
ssl: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'pop3',
options: {
host: "mail.#{domain}",
port: 995,
ssl: true,
user: user,
password: params[:password],
},
},
{
adapter: 'pop3',
options: {
host: "mail.#{domain}",
port: 995,
ssl: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'pop3',
options: {
host: "pop.#{domain}",
port: 995,
ssl: true,
user: user,
password: params[:password],
},
},
{
adapter: 'pop3',
options: {
host: "pop.#{domain}",
port: 995,
ssl: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'pop3',
options: {
host: "pop3.#{domain}",
port: 995,
ssl: true,
user: user,
password: params[:password],
},
},
{
adapter: 'pop3',
options: {
host: "pop3.#{domain}",
port: 995,
ssl: true,
user: params[:email],
password: params[:password],
},
},
]
inbound_map = inbound_map + inbound_auto
settings = {}
success = false
inbound_map.each {|config|
logger.info "INBOUND PROBE: #{config.inspect}"
result = email_probe_inbound( config )
logger.info "INBOUND RESULT: #{result.inspect}"
next if result[:result] != 'ok'
success = true
settings[:inbound] = config
break
}
if !success
render json: {
result: 'failed',
}
return
end
# probe outbound
outbound_map = []
if mail_exchangers && mail_exchangers[0] && mail_exchangers[0][0]
outbound_mx = [
{
adapter: 'smtp',
options: {
host: mail_exchangers[0][0],
port: 25,
start_tls: true,
user: user,
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: mail_exchangers[0][0],
port: 25,
start_tls: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: mail_exchangers[0][0],
port: 465,
start_tls: true,
user: user,
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: mail_exchangers[0][0],
port: 465,
start_tls: true,
user: params[:email],
password: params[:password],
},
},
]
outbound_map = outbound_map + outbound_mx
end
outbound_auto = [
{
adapter: 'smtp',
options: {
host: "mail.#{domain}",
port: 25,
start_tls: true,
user: user,
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "mail.#{domain}",
port: 25,
start_tls: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "mail.#{domain}",
port: 465,
start_tls: true,
user: user,
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "mail.#{domain}",
port: 465,
start_tls: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "smtp.#{domain}",
port: 25,
start_tls: true,
user: user,
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "smtp.#{domain}",
port: 25,
start_tls: true,
user: params[:email],
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "smtp.#{domain}",
port: 465,
start_tls: true,
user: user,
password: params[:password],
},
},
{
adapter: 'smtp',
options: {
host: "smtp.#{domain}",
port: 465,
start_tls: true,
user: params[:email],
password: params[:password],
},
},
]
success = false
outbound_map.each {|config|
logger.info "OUTBOUND PROBE: #{config.inspect}"
result = email_probe_outbound( config, params[:email] )
logger.info "OUTBOUND RESULT: #{result.inspect}"
next if result[:result] != 'ok'
success = true
settings[:outbound] = config
break
}
if !success
render json: {
result: 'failed',
}
return
end
render json: {
result: 'ok',
setting: settings,
}
end
def email_outbound
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# validate params
if !params[:adapter]
render json: {
result: 'invalid',
}
return
end
# connection test
result = email_probe_outbound( params, params[:email] )
render json: result
end
def email_inbound
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# validate params
if !params[:adapter]
render json: {
result: 'invalid',
}
return
end
# connection test
result = email_probe_inbound( params )
render json: result
end
def email_verify
# check admin permissions
return if deny_if_not_role(Z_ROLENAME_ADMIN)
# send verify email to inbox
if !params[:subject]
subject = '#' + rand(99_999_999_999).to_s
else
subject = params[:subject]
end
result = email_probe_outbound( params[:outbound], params[:meta][:email], subject )
(1..5).each {
sleep 10
# fetch mailbox
found = nil
begin
if params[:inbound][:adapter] =~ /^imap$/i
found = Channel::IMAP.new.fetch( { options: params[:inbound][:options] }, 'verify', subject )
else
found = Channel::POP3.new.fetch( { options: params[:inbound][:options] }, 'verify', subject )
end
rescue => e
render json: {
result: 'invalid',
message: e.to_s,
subject: subject,
}
return # rubocop:disable Lint/NonLocalExitFromIterator
end
next if !found
next if found != 'verify ok'
# remember address
address = EmailAddress.where( email: params[:meta][:email] ).first
if !address
address = EmailAddress.first
end
if address
address.update_attributes(
realname: params[:meta][:realname],
email: params[:meta][:email],
active: 1,
updated_by_id: 1,
created_by_id: 1,
)
else
EmailAddress.create(
realname: params[:meta][:realname],
email: params[:meta][:email],
active: 1,
updated_by_id: 1,
created_by_id: 1,
)
end
# store mailbox
Channel.create(
area: 'Email::Inbound',
adapter: params[:inbound][:adapter],
options: params[:inbound][:options],
group_id: 1,
active: 1,
updated_by_id: 1,
created_by_id: 1,
)
# save settings
if params[:outbound][:adapter] =~ /^smtp$/i
smtp = Channel.where( adapter: 'SMTP', area: 'Email::Outbound' ).first
smtp.options = params[:outbound][:options]
smtp.active = true
smtp.save!
sendmail = Channel.where( adapter: 'Sendmail' ).first
sendmail.active = false
sendmail.save!
else
sendmail = Channel.where( adapter: 'Sendmail', area: 'Email::Outbound' ).first
sendmail.options = {}
sendmail.active = true
sendmail.save!
smtp = Channel.where( adapter: 'SMTP' ).first
smtp.active = false
smtp.save
end
render json: {
result: 'ok',
}
return # rubocop:disable Lint/NonLocalExitFromIterator
}
# check delivery for 30 sek.
render json: {
result: 'invalid',
message: 'Verification Email not found in mailbox.',
subject: subject,
}
end
private
def email_probe_outbound(params, email, subject = nil)
# validate params
if !params[:adapter]
result = {
result: 'invalid',
message: 'Invalid, need adapter!',
}
return result
end
if subject
mail = {
:from => email,
:to => email,
:subject => "Zammad Getting started Test Email #{subject}",
: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.",
'x-zammad-ignore' => 'true',
}
else
mail = {
from: email,
to: 'emailtrytest@znuny.com',
subject: 'test',
body: 'test',
}
end
# test connection
translation_map = {
'authentication failed' => 'Authentication failed!',
'Incorrect username' => 'Authentication failed!',
'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] =~ /^smtp$/i
# in case, fill missing params
if !params[:options].key?(:port)
params[:options][:port] = 25
end
if !params[:options].key?(:ssl)
params[:options][:ssl] = true
end
begin
Channel::SMTP.new.send(
mail,
{
options: params[:options]
}
)
rescue => e
# check if sending email was ok, but mailserver rejected
if !subject
white_map = {
'Recipient address rejected' => true,
}
white_map.each {|key, _message|
next if e.message !~ /#{Regexp.escape(key)}/i
result = {
result: 'ok',
settings: params,
notice: e.message,
}
return result
}
end
message_human = ''
translation_map.each {|key, message|
if e.message =~ /#{Regexp.escape(key)}/i
message_human = message
end
}
result = {
result: 'invalid',
settings: params,
message: e.message,
message_human: message_human,
}
return result
end
result = {
result: 'ok',
}
return result
end
begin
Channel::Sendmail.new.send(
mail,
nil
)
rescue => e
message_human = ''
translation_map.each {|key, message|
if e.message =~ /#{Regexp.escape(key)}/i
message_human = message
end
}
result = {
result: 'invalid',
settings: params,
message: e.message,
message_human: message_human,
}
return result
end
result = {
result: 'ok',
}
result
end
def email_probe_inbound(params)
# validate params
if !params[:adapter]
fail 'need :adapter param'
end
# connection test
translation_map = {
'authentication failed' => 'Authentication failed!',
'Incorrect username' => 'Authentication failed!',
'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
begin
Channel::IMAP.new.fetch( { options: params[:options] }, 'check' )
rescue => e
message_human = ''
translation_map.each {|key, message|
if e.message =~ /#{Regexp.escape(key)}/i
message_human = message
end
}
result = {
result: 'invalid',
settings: params,
message: e.message,
message_human: message_human,
}
return result
end
result = {
result: 'ok',
}
return result
end
begin
Channel::POP3.new.fetch( { options: params[:options] }, 'check' )
rescue => e
message_human = ''
translation_map.each {|key, message|
if e.message =~ /#{Regexp.escape(key)}/i
message_human = message
end
}
result = {
result: 'invalid',
settings: params,
message: e.message,
message_human: message_human,
}
return result
end
result = {
result: 'ok',
}
result
end
def mxers(domain)
begin
mxs = Resolv::DNS.open do |dns|
ress = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
ress.map { |r| [r.exchange.to_s, IPSocket.getaddress(r.exchange.to_s), r.preference] }
end
rescue => e
logger.error e.message
logger.error e.backtrace.inspect
end
mxs
end
def auto_wizard_enabled_response
return false if !AutoWizard.enabled?
render json: {
auto_wizard: true
}
true
end
def setup_done
#return false
count = User.all.count()
done = true
if count <= 2
done = false
end
done
end
def setup_done_response
return false if !setup_done
# get all groups
groups = Group.where( active: true )
# get email addresses
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,
}
true
end
end