123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984 |
- # 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
- 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
- }
- # 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
|