diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 99741dc47..20297b184 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -127,6 +127,17 @@ test:integration:email_deliver: - ruby -I test/ test/integration/email_deliver_test.rb - rake db:drop +test:integration:email_keep_on_server: + stage: test + tags: + - core + script: + - export RAILS_ENV=test + - rake db:create + - rake db:migrate + - ruby -I test/ test/integration/email_keep_on_server_test.rb + - rake db:drop + test:integration:twitter: stage: test tags: diff --git a/app/assets/javascripts/app/controllers/_channel/email.coffee b/app/assets/javascripts/app/controllers/_channel/email.coffee index 99a71620e..a6855923a 100644 --- a/app/assets/javascripts/app/controllers/_channel/email.coffee +++ b/app/assets/javascripts/app/controllers/_channel/email.coffee @@ -560,21 +560,24 @@ class App.ChannelEmailAccountWizard extends App.WizardModal # inbound configureAttributesInbound = [ - { name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound }, - { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, - { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off', }, - { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true }, - { name: 'options::ssl', display: 'SSL', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' }, - { name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' }, - { name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false }, + { name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound }, + { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, + { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off' }, + { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true }, + { name: 'options::ssl', display: 'SSL', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' }, + { name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' }, + { name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, item_class: 'formGroup--halfSize' }, + { name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false, item_class: 'formGroup--halfSize' }, ] showHideFolder = (params, attribute, attributes, classname, form, ui) -> return if !params if params.adapter is 'imap' ui.show('options::folder') + ui.show('options::keep_on_server') return ui.hide('options::folder') + ui.hide('options::keep_on_server') handlePort = (params, attribute, attributes, classname, form, ui) -> return if !params @@ -606,9 +609,10 @@ class App.ChannelEmailAccountWizard extends App.WizardModal # fill user / password based on intro info channel_used = { options: {} } if @account['meta'] - channel_used['options']['user'] = @account['meta']['email'] - channel_used['options']['password'] = @account['meta']['password'] - channel_used['options']['folder'] = @account['meta']['folder'] + channel_used['options']['user'] = @account['meta']['email'] + channel_used['options']['password'] = @account['meta']['password'] + channel_used['options']['folder'] = @account['meta']['folder'] + channel_used['options']['keep_on_server'] = @account['meta']['keep_on_server'] # show used backend @$('.base-outbound-settings').html('') @@ -670,7 +674,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal for key, value of data.setting @account[key] = value - if data.content_messages && data.content_messages > 0 + if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true) message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages) @$('.js-inbound-acknowledge .js-message').html(message) @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-intro') @@ -724,7 +728,7 @@ class App.ChannelEmailAccountWizard extends App.WizardModal # remember account settings @account.inbound = params - if data.content_messages && data.content_messages > 0 + if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true) message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages) @$('.js-inbound-acknowledge .js-message').html(message) @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-inbound') diff --git a/app/assets/javascripts/app/controllers/getting_started.coffee b/app/assets/javascripts/app/controllers/getting_started.coffee index c7d236267..8c2fbb71d 100644 --- a/app/assets/javascripts/app/controllers/getting_started.coffee +++ b/app/assets/javascripts/app/controllers/getting_started.coffee @@ -450,8 +450,8 @@ class EmailNotification extends App.WizardFullScreen if adapter is 'smtp' configureAttributesOutbound = [ { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true }, - { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password' }, - { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true }, + { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off' }, + { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', single: true }, { name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false }, ] @form = new App.ControllerForm( @@ -671,20 +671,24 @@ class ChannelEmail extends App.WizardFullScreen # inbound configureAttributesInbound = [ - { name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound }, - { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, - { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', }, - { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true }, - { name: 'options::ssl', display: 'SSL', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' }, - { name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' }, + { name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound }, + { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false }, + { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off', }, + { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'off', single: true }, + { name: 'options::ssl', display: 'SSL', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' }, + { name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' }, + { name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, item_class: 'formGroup--halfSize' }, + { name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false, item_class: 'formGroup--halfSize' }, ] showHideFolder = (params, attribute, attributes, classname, form, ui) -> return if !params if params.adapter is 'imap' ui.show('options::folder') + ui.show('options::keep_on_server') return ui.hide('options::folder') + ui.hide('options::keep_on_server') handlePort = (params, attribute, attributes, classname, form, ui) -> return if !params @@ -700,7 +704,7 @@ class ChannelEmail extends App.WizardFullScreen return new App.ControllerForm( - el: @$('.base-inbound-settings'), + el: @$('.base-inbound-settings') model: configure_attributes: configureAttributesInbound className: '' @@ -716,8 +720,10 @@ class ChannelEmail extends App.WizardFullScreen # fill user / password based on intro info channel_used = { options: {} } if @account['meta'] - channel_used['options']['user'] = @account['meta']['email'] - channel_used['options']['password'] = @account['meta']['password'] + channel_used['options']['user'] = @account['meta']['email'] + channel_used['options']['password'] = @account['meta']['password'] + channel_used['options']['folder'] = @account['meta']['folder'] + channel_used['options']['keep_on_server'] = @account['meta']['keep_on_server'] # show used backend @$('.base-outbound-settings').html('') @@ -725,8 +731,8 @@ class ChannelEmail extends App.WizardFullScreen if adapter is 'smtp' configureAttributesOutbound = [ { name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true }, - { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', }, - { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true }, + { name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', }, + { name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', single: true }, { name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false }, ] @form = new App.ControllerForm( @@ -745,7 +751,7 @@ class ChannelEmail extends App.WizardFullScreen @account.meta = params @disable(e) - @$('.js-probe .js-email').text( params.email ) + @$('.js-probe .js-email').text(params.email) @showSlide('js-probe') @ajax( @@ -760,7 +766,7 @@ class ChannelEmail extends App.WizardFullScreen for key, value of data.setting @account[key] = value - if data.content_messages && data.content_messages > 0 + if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true) message = App.i18n.translateContent('We have already found %s email(s) in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages) @$('.js-inbound-acknowledge .js-message').html(message) @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-intro') @@ -809,7 +815,7 @@ class ChannelEmail extends App.WizardFullScreen # remember account settings @account.inbound = params - if data.content_messages && data.content_messages > 0 + if data.content_messages && data.content_messages > 0 && (!@account['inbound']['options'] || @account['inbound']['options']['keep_on_server'] isnt true) message = App.i18n.translateContent('We have already found %s emails in your mailbox. Zammad will move it all from your mailbox into Zammad.', data.content_messages) @$('.js-inbound-acknowledge .js-message').html(message) @$('.js-inbound-acknowledge .js-back').attr('data-slide', 'js-inbound') diff --git a/app/assets/javascripts/app/views/getting_started/email.jst.eco b/app/assets/javascripts/app/views/getting_started/email.jst.eco index e3ef082f8..1d2d0cb50 100644 --- a/app/assets/javascripts/app/views/getting_started/email.jst.eco +++ b/app/assets/javascripts/app/views/getting_started/email.jst.eco @@ -63,7 +63,7 @@
-
+

<%- @T('Email Inbound') %>

diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss index 8da4e8e50..2a6020b51 100644 --- a/app/assets/stylesheets/zammad.scss +++ b/app/assets/stylesheets/zammad.scss @@ -6457,6 +6457,10 @@ footer { width: 400px; padding-bottom: 18px; margin-bottom: 20px; + + &.wizard-slide--large { + width: 460px; + } } .wizard h2 { diff --git a/app/models/channel/driver/imap.rb b/app/models/channel/driver/imap.rb index d2e0043bc..344b971b9 100644 --- a/app/models/channel/driver/imap.rb +++ b/app/models/channel/driver/imap.rb @@ -52,6 +52,7 @@ example host: 'outlook.office365.com', user: 'xxx@znuny.onmicrosoft.com', password: 'xxx', + keep_on_server: true, } channel = Channel.last instance = Channel::Driver::Imap.new @@ -60,13 +61,18 @@ example =end def fetch (options, channel, check_type = '', verify_string = '') - ssl = true - port = 993 + ssl = true + port = 993 + keep_on_server = false + folder = 'INBOX' + if options[:keep_on_server] == true || options[:keep_on_server] == 'true' + keep_on_server = true + end if options.key?(:ssl) && options[:ssl] == false ssl = false port = 143 end - if options.key?(:port) && !options[:port].empty? + if options.key?(:port) && options[:port].present? port = options[:port] # disable ssl for non ssl ports @@ -74,8 +80,11 @@ example ssl = false end end + if options[:folder].present? + folder = options[:folder] + end - Rails.logger.info "fetching imap (#{options[:host]}/#{options[:user]} port=#{port},ssl=#{ssl},folder=#{options[:folder]})" + Rails.logger.info "fetching imap (#{options[:host]}/#{options[:user]} port=#{port},ssl=#{ssl},folder=#{folder},keep_on_server=#{keep_on_server})" # on check, reduce open_timeout to have faster probing timeout = 45 @@ -90,17 +99,17 @@ example @imap.login(options[:user], options[:password]) # select folder - if !options[:folder] || options[:folder].empty? - @imap.select('INBOX') - else - @imap.select(options[:folder]) - end + @imap.select(folder) # sort messages by date on server (if not supported), if not fetch messages via search (first in, first out) + filter = ['ALL'] + if keep_on_server && check_type != 'check' && check_type != 'verify' + filter = %w(NOT SEEN) + end begin - message_ids = @imap.sort(['DATE'], ['ALL'], 'US-ASCII') + message_ids = @imap.sort(['DATE'], filter, 'US-ASCII') rescue - message_ids = @imap.search(['ALL']) + message_ids = @imap.search(filter) end # check mode only @@ -168,9 +177,8 @@ example message_ids.each do |message_id| count += 1 Rails.logger.info " - message #{count}/#{count_all}" - #Rails.logger.info msg.to_s - message_meta = @imap.fetch(message_id, ['RFC822.SIZE', 'FLAGS', 'INTERNALDATE'])[0] + message_meta = @imap.fetch(message_id, ['RFC822.SIZE', 'ENVELOPE', 'FLAGS', 'INTERNALDATE'])[0] # ignore to big messages info = too_big?(message_meta, count, count_all) @@ -182,14 +190,23 @@ example # ignore deleted messages next if deleted?(message_meta, count, count_all) + # ignore already imported + next if already_imported?(message_id, message_meta, count, count_all, keep_on_server) + # delete email from server after article was created msg = @imap.fetch(message_id, 'RFC822')[0].attr['RFC822'] next if !msg process(channel, msg, false) - @imap.store(message_id, '+FLAGS', [:Deleted]) + if !keep_on_server + @imap.store(message_id, '+FLAGS', [:Deleted]) + else + @imap.store(message_id, '+FLAGS', [:Seen]) + end count_fetched += 1 end - @imap.expunge() + if !keep_on_server + @imap.expunge() + end disconnect if count.zero? Rails.logger.info ' - no message' @@ -209,6 +226,20 @@ example private + def already_imported?(message_id, message_meta, count, count_all, keep_on_server) + return false if !keep_on_server + return false if !message_meta.attr + return false if !message_meta.attr['ENVELOPE'] + local_message_id = message_meta.attr['ENVELOPE'].message_id + return false if local_message_id.blank? + local_message_id_md5 = Digest::MD5.hexdigest(local_message_id) + article = Ticket::Article.where(message_id_md5: local_message_id_md5).order('created_at DESC, id DESC').limit(1).first + return false if !article + @imap.store(message_id, '+FLAGS', [:Seen]) + Rails.logger.info " - ignore message #{count}/#{count_all} - because message message id already imported" + true + end + def deleted?(message_meta, count, count_all) return false if !message_meta.attr['FLAGS'].include?(:Deleted) Rails.logger.info " - ignore message #{count}/#{count_all} - because message has already delete flag" diff --git a/test/integration/email_deliver_test.rb b/test/integration/email_deliver_test.rb index 6d13296b6..440dc550b 100644 --- a/test/integration/email_deliver_test.rb +++ b/test/integration/email_deliver_test.rb @@ -4,16 +4,16 @@ require 'test_helper' class EmailDeliverTest < ActiveSupport::TestCase test 'basic check' do - if !ENV['MAIL_SERVER'] + if ENV['MAIL_SERVER'].blank? raise "Need MAIL_SERVER as ENV variable like export MAIL_SERVER='mx.example.com'" end - if !ENV['MAIL_SERVER_ACCOUNT'] + if ENV['MAIL_SERVER_ACCOUNT'].blank? raise "Need MAIL_SERVER_ACCOUNT as ENV variable like export MAIL_SERVER_ACCOUNT='user:somepass'" end server_login = ENV['MAIL_SERVER_ACCOUNT'].split(':')[0] server_password = ENV['MAIL_SERVER_ACCOUNT'].split(':')[1] - email_address = EmailAddress.create( + email_address = EmailAddress.create!( realname: 'me Helpdesk', email: "me#{rand(999_999_999)}@example.com", updated_by_id: 1, @@ -27,7 +27,7 @@ class EmailDeliverTest < ActiveSupport::TestCase created_by_id: 1, ) - channel = Channel.create( + channel = Channel.create!( area: 'Email::Account', group_id: group.id, options: { @@ -50,9 +50,9 @@ class EmailDeliverTest < ActiveSupport::TestCase ) email_address.channel_id = channel.id - email_address.save + email_address.save! - ticket1 = Ticket.create( + ticket1 = Ticket.create!( title: 'some delivery test', group: group, customer_id: 2, @@ -63,7 +63,7 @@ class EmailDeliverTest < ActiveSupport::TestCase ) assert(ticket1, 'ticket created') - article1 = Ticket::Article.create( + article1 = Ticket::Article.create!( ticket_id: ticket1.id, to: 'some_recipient@example_not_existing_what_ever.com', subject: 'some subject', @@ -189,7 +189,7 @@ class EmailDeliverTest < ActiveSupport::TestCase # remove background jobs Delayed::Job.destroy_all - article2 = Ticket::Article.create( + article2 = Ticket::Article.create!( ticket_id: ticket1.id, to: 'some_recipient@example_not_existing_what_ever.com', subject: 'some subject2', diff --git a/test/integration/email_keep_on_server_test.rb b/test/integration/email_keep_on_server_test.rb new file mode 100644 index 000000000..99d8069b9 --- /dev/null +++ b/test/integration/email_keep_on_server_test.rb @@ -0,0 +1,232 @@ +# encoding: utf-8 +require 'test_helper' +require 'net/imap' + +class EmailKeepOnServerTest < ActiveSupport::TestCase + setup do + + if ENV['KEEP_ON_MAIL_SERVER'].blank? + raise "Need KEEP_ON_MAIL_SERVER as ENV variable like export KEEP_ON_MAIL_SERVER='mx.example.com'" + end + if ENV['KEEP_ON_MAIL_SERVER_ACCOUNT'].blank? + raise "Need KEEP_ON_MAIL_SERVER_ACCOUNT as ENV variable like export KEEP_ON_MAIL_SERVER_ACCOUNT='user:somepass'" + end + @server_login = ENV['KEEP_ON_MAIL_SERVER_ACCOUNT'].split(':')[0] + @server_password = ENV['KEEP_ON_MAIL_SERVER_ACCOUNT'].split(':')[1] + + @folder = "keep_on_mail_server_#{rand(999_999_999)}" + + email_address = EmailAddress.create!( + realname: 'me Helpdesk', + email: "me#{rand(999_999_999)}@example.com", + updated_by_id: 1, + created_by_id: 1, + ) + + group = Group.create_or_update( + name: 'KeepOnServerTest', + email_address_id: email_address.id, + updated_by_id: 1, + created_by_id: 1, + ) + + @channel = Channel.create!( + area: 'Email::Account', + group_id: group.id, + options: { + inbound: { + adapter: 'imap', + options: { + host: ENV['KEEP_ON_MAIL_SERVER'], + user: @server_login, + password: @server_password, + ssl: true, + folder: @folder, + #keep_on_server: true, + } + }, + outbound: { + adapter: 'sendmail' + } + }, + active: true, + updated_by_id: 1, + created_by_id: 1, + ) + email_address.channel_id = @channel.id + email_address.save! + + end + + test 'keep on server' do + @channel.options[:inbound][:options][:keep_on_server] = true + @channel.save! + + # clean mailbox + imap = Net::IMAP.new(ENV['KEEP_ON_MAIL_SERVER'], 993, true, nil, false) + imap.login(@server_login, @server_password) + imap.create(@folder) + imap.select(@folder) + + # put unseen message in it + imap.append(@folder, "Subject: hello1 +From: shugo@example.com +To: shugo@example.com +Message-ID: + +hello world +".gsub(/\n/, "\r\n"), [], Time.zone.now) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + message_meta = imap.fetch(1, ['FLAGS'])[0].attr + assert_not(message_meta['FLAGS'].include?(:Seen)) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count + 1, Ticket::Article.count) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + message_meta = imap.fetch(1, ['RFC822.HEADER', 'FLAGS'])[0].attr + assert(message_meta['FLAGS'].include?(:Seen)) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count, Ticket::Article.count) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + # put unseen message in it + imap.append(@folder, "Subject: hello2 +From: shugo@example.com +To: shugo@example.com +Message-ID: + +hello world +".gsub(/\n/, "\r\n"), [], Time.zone.now) + + message_meta = imap.fetch(1, ['FLAGS'])[0].attr + assert(message_meta['FLAGS'].include?(:Seen)) + message_meta = imap.fetch(2, ['FLAGS'])[0].attr + assert_not(message_meta['FLAGS'].include?(:Seen)) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count + 1, Ticket::Article.count) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(2, message_ids.count) + + message_meta = imap.fetch(1, ['FLAGS'])[0].attr + assert(message_meta['FLAGS'].include?(:Seen)) + message_meta = imap.fetch(2, ['FLAGS'])[0].attr + assert(message_meta['FLAGS'].include?(:Seen)) + + # set messages to not seen + imap.store(1, '-FLAGS', [:Seen]) + imap.store(2, '-FLAGS', [:Seen]) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count, Ticket::Article.count) + + imap.delete(@folder) + @channel.destroy! + end + + test 'keep not on server' do + @channel.options[:inbound][:options][:keep_on_server] = false + @channel.save! + + # clean mailbox + imap = Net::IMAP.new(ENV['KEEP_ON_MAIL_SERVER'], 993, true, nil, false) + imap.login(@server_login, @server_password) + imap.create(@folder) + imap.select(@folder) + + # put unseen message in it + imap.append(@folder, "Subject: hello1 +From: shugo@example.com +To: shugo@example.com +Message-ID: + +hello world +".gsub(/\n/, "\r\n"), [], Time.zone.now) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + message_meta = imap.fetch(1, ['FLAGS'])[0].attr + assert_not(message_meta['FLAGS'].include?(:Seen)) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count + 1, Ticket::Article.count) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + # put unseen message in it + imap.append(@folder, "Subject: hello2 +From: shugo@example.com +To: shugo@example.com +Message-ID: + +hello world +".gsub(/\n/, "\r\n"), [], Time.zone.now) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + message_meta = imap.fetch(1, ['FLAGS'])[0].attr + assert_not(message_meta['FLAGS'].include?(:Seen)) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count + 1, Ticket::Article.count) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + # put unseen message in it + imap.append(@folder, "Subject: hello2 +From: shugo@example.com +To: shugo@example.com +Message-ID: + +hello world +".gsub(/\n/, "\r\n"), [], Time.zone.now) + + # verify if message is still on server + message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII') + assert_equal(1, message_ids.count) + + # fetch messages + article_count = Ticket::Article.count + @channel.fetch(true) + assert_equal(article_count + 1, Ticket::Article.count) + + imap.delete(@folder) + @channel.destroy! + + end + +end