#coding:ISO-8859-1

require 'socket'
require_relative 'common.rb'
#{{{{Konstanten
$maxList=30
$compact=false
$weeks=2
$showAttachmentsInList=false
$currentFolder=""
$targetFolder=""
$mailDir="C:/Mails"
$mailDirWin="C:\\Mails"
$helpFile="C:/Work_Ruby/outlookhelp.txt"
$localAddressBook="C:/Work_Ruby/outlookaddressbook.csv"
$localAddressHash=Hash.new()
$mailList=""        # eigentlich Array
$appointmentList="" # Array
$weekSeconds=3600*24*7
$foundSmtpAddress=nil
$foundMailAddresses=nil
FROM=true
TO=false
#}}}}
def attach(mail)#{{{{
  begin
    #-------------------------------------------------------------------------------
    # Attachments hinzufuegen ?
    #-------------------------------------------------------------------------------
    while(useryes?("Attachment ?"))
      out("Attachment ? ... (Format: C:/Hugo/Willi.pdf)")
      attachment=$client.gets.chop
      if(! File.file?(attachment))
        $client.puts("attach: Attachment |#{attachment}| does not exist")
      else
        mail.Attachments.Add(attachment,OutlookConst::OlByValue)
        mail.Attachments.each{ |attachment|
          $client.puts("Attachment: #{attachment.FileName}")
        }
      end
    end
  rescue => exception
    $client.puts("attach: Problem #{exception}")
  end
end#}}}}
def chooseRecipientsInvitees(object,type)#{{{{
  begin
    commands="... done,purge,remove <index>,show,<recipient>,cc_<recipient>, gal|pal <name>, gal|pal \"<name with blanks>\""
    if(type == "mail")
      isMail=true
    elsif (type == "appointment")
      isMail=false
    else
      $client.puts("Interner Fehler: Ungueltiger Typ: #{type}")
      return
    end
    #-------------------------------------------------------------------------------
    # Empfaenger (To und Cc)
    #-------------------------------------------------------------------------------
    out("Empfaenger eingeben ... #{commands}")
    loop{
      line=$client.gets.chop # Newline abschneiden
      if(line == "done")
        break
      #------------------------------------------------------------------------------------
      # Bestimmten Empfaenger loeschen ?
      #------------------------------------------------------------------------------------
      elsif(line.match(/^remove\s+(\d+)/)) 
        index=$1.to_i
        recipient=object.Recipients(index)
        $client.puts("removing recipient #{index}: #{recipient.Name}")
        object.Recipients.Remove(index)
      #------------------------------------------------------------------------------------
      # Alle Empfaenger loeschen ?
      #------------------------------------------------------------------------------------
      elsif(line == "purge") 
        #----------------------------------------------------------------------------------
        # Cave: Loeschung beeinflusst Indizierung aller Elemente, daher ist keine einfache
        #       for-each-Loesung moeglich
        #----------------------------------------------------------------------------------
        while(object.Recipients.Count > 0)
          recipient=object.Recipients(1)
          $client.puts("removing #{recipient.Name} ...")
          object.Recipients.Remove(1)
        end
        $client.puts("all recipients removed")
      #------------------------------------------------------------------------------------
      # Alle Empfaenger anzeigen
      #------------------------------------------------------------------------------------
      elsif(line == "show")
        object.Recipients.each{ |recipient|
          $client.printf("%2d: %5.5s: %s\n",recipient.Index,(recipient.Type == 1) ? "To" : "Cc",recipient.Name)
        }
      #------------------------------------------------------------------------------------
      # Suche in globalem Adressbuch
      #------------------------------------------------------------------------------------
      elsif(line.match(/^gal\s+"([^"]+)"/i) or line.match(/^gal\s+(\S+)/i))
        $foundSmtpAddress=nil
        searchGalContact($1.to_s)
        if($foundSmtpAddress and useryes?("#{$foundSmtpAddress} uebernehmen ?"))
          if(useryes?("Als Cc ?"))
            if(isMail)
              object.Recipients.add($foundSmtpAddress).Type=OutlookConst::OlCC
            else
              object.Recipients.add($foundSmtpAddress).Type=OutlookConst::OlOptional
            end
            $client.puts("Added #{line} as cc-recipient")
          else
            if(isMail)
              object.Recipients.add($foundSmtpAddress)
              $client.puts("Added #{line} as recipient")
            else
              object.Recipients.add($foundSmtpAddress).Type==OutlookConst::OlRequired
              $client.puts("Added #{line} as required recipient")
            end
          end
        else
          $client.puts("#{$foundSmtpAddress} forfeited")
        end
      #------------------------------------------------------------------------------------
      # Suche in Kontakten
      #------------------------------------------------------------------------------------
      elsif(line.match(/^pal\s+"([^"]+)"/i) or line.match(/^pal\s+(\S+)/i))
        pattern=$1.to_s
        $foundMailAddresses=Array.new()
        #---------------------------------------------------------------------------------
        # Zuerst Suche im (zu Beginn geladenen) lokalen Adressbuch ...
        #---------------------------------------------------------------------------------
        locallyFound=false
        $localAddressHash.each{ |name,address|
          if(name.match(/#{pattern}/i))
            locallyFound=true
            #-----------------------------------------------------------------------------
            # Verteilerliste ? (duch ',' getrennte Adressen)
            #-----------------------------------------------------------------------------
            if(address.match(/,/))
              addressArr=address.split(/,/)
              $client.puts("Hit in local address book: contact-group #{name}")
              addressArr.each{ |memberAddress|
                $foundMailAddresses.append(memberAddress.strip())
                $client.puts("Multiple hit in local address book: #{memberAddress.strip()}")
              }
            else
              $foundMailAddresses.append(address)    
              $client.puts("Hit in local address book: #{address}")
            end
            break
          end
        }
        #---------------------------------------------------------------------------------
        # ... sonst in den persoenlichen Kontakten
        #---------------------------------------------------------------------------------
        if(! locallyFound)
          searchPalContact(pattern)
        end
        #---------------------------------------------------------------------------------
        if($foundMailAddresses.length() > 0)
          $foundMailAddresses.each{ |address|
            if(useryes?("#{address} uebernehmen ?"))
              if(useryes?("Als Cc ?"))
                if(isMail)
                  object.Recipients.add(address).Type=OutlookConst::OlCC
                else
                  object.Recipients.add(address).Type=OutlookConst::OlOptional
                end
                $client.puts("Added #{address} as cc-recipient")
              else
                if(isMail)
                  object.Recipients.add(address)
                  $client.puts("Added #{address} as recipient")
                else
                  object.Recipients.add(address).Type==OutlookConst::OlRequired
                  $client.puts("Added #{address} as required recipient")
                end
              end
            end
          }
        else
          $client.puts("No mail-address found")
        end
      #------------------------------------------------------------------------------------
      # Cc-Empfaenger hinzufuegen
      #------------------------------------------------------------------------------------
      elsif(line.match(/cc_(\S+)/))
        line=$1
        if(isMail)
          object.Recipients.add(line).Type=OutlookConst::OlCC
        else
          object.Recipients.add(line).Type=OutlookConst::OlOptional
        end
        $client.puts("Added #{line} as cc-recipient")
      #------------------------------------------------------------------------------------
      # To-Empfaenger hinzufuegen
      #------------------------------------------------------------------------------------
      elsif(! line.empty?)
        if(isMail)
          object.Recipients.add(line)
          $client.puts("Added #{line} as recipient")
        else
          object.Recipients.add(line).Type==OutlookConst::OlRequired
          $client.puts("Added #{line} as required recipient")
        end
      else
        $client.puts("Bitte Befehl eingeben ... #{commands}")
      end
    }
  rescue => exception
    $client.puts("chooseRecipientsInvitees: Problem #{exception}")
  end
end#}}}}
def chooseRecipients(mail)#{{{{
  begin
    commands="... done,purge,remove <index>,show,<recipient>,cc_<recipient>, gal|pal <name>, gal|pal \"<name with blanks>\""
    #-------------------------------------------------------------------------------
    # Empfaenger (To und Cc)
    #-------------------------------------------------------------------------------
    out("Empfaenger eingeben ... #{commands}")
    loop{
      line=$client.gets.chop # Newline abschneiden
      if(line == "done")
        break
      #------------------------------------------------------------------------------------
      # Bestimmten Empfaenger loeschen ?
      #------------------------------------------------------------------------------------
      elsif(line.match(/^remove\s+(\d+)/)) 
        index=$1.to_i
        recipient=mail.Recipients(index)
        $client.puts("removing recipient #{index}: #{recipient.Name}")
        mail.Recipients.Remove(index)
      #------------------------------------------------------------------------------------
      # Alle Empfaenger loeschen ?
      #------------------------------------------------------------------------------------
      elsif(line == "purge") 
        #----------------------------------------------------------------------------------
        # Cave: Loeschung beeinflusst Indizierung aller Elemente, daher ist keine einfache
        #       for-each-Loesung moeglich
        #----------------------------------------------------------------------------------
        while(mail.Recipients.Count > 0)
          recipient=mail.Recipients(1)
          $client.puts("removing #{recipient.Name} ...")
          mail.Recipients.Remove(1)
        end
        $client.puts("all recipients removed")
      #------------------------------------------------------------------------------------
      # Alle Empfaenger anzeigen
      #------------------------------------------------------------------------------------
      elsif(line == "show")
        mail.Recipients.each{ |recipient|
          $client.printf("%2d: %5.5s: %s\n",recipient.Index,(recipient.Type == 1) ? "To" : "Cc",recipient.Name)
        }
      #------------------------------------------------------------------------------------
      # Suche in globalem Adressbuch
      #------------------------------------------------------------------------------------
      elsif(line.match(/^gal\s+"([^"]+)"/i) or line.match(/^gal\s+(\S+)/i))
        $foundSmtpAddress=nil
        searchGalContact($1.to_s)
        if($foundSmtpAddress and useryes?("#{$foundSmtpAddress} uebernehmen ?"))
          if(useryes?("Als Cc ?"))
            mail.Recipients.add($foundSmtpAddress).Type=OutlookConst::OlCC
            $client.puts("Added #{line} as cc-recipient")
          else
            mail.Recipients.add($foundSmtpAddress)
            $client.puts("Added #{line} as recipient")
          end
        else
          $client.puts("#{$foundSmtpAddress} forfeited")
        end
      #------------------------------------------------------------------------------------
      # Suche in Kontakten
      #------------------------------------------------------------------------------------
      elsif(line.match(/^pal\s+"([^"]+)"/i) or line.match(/^pal\s+(\S+)/i))
        pattern=$1.to_s
        $foundMailAddresses=Array.new()
        #---------------------------------------------------------------------------------
        # Zuerst Suche im (zu Beginn geladenen) lokalen Adressbuch ...
        #---------------------------------------------------------------------------------
        locallyFound=false
        $localAddressHash.each{ |name,address|
          if(name.match(/#{pattern}/i))
            locallyFound=true
            #-----------------------------------------------------------------------------
            # Verteilerliste ? (duch ',' getrennte Adressen)
            #-----------------------------------------------------------------------------
            if(address.match(/,/))
              addressArr=address.split(/,/)
              $client.puts("Hit in local address book: contact-group #{name}")
              addressArr.each{ |memberAddress|
                $foundMailAddresses.append(memberAddress.strip())
              }
            else
              $foundMailAddresses.append(address)    
              $client.puts("Hit in local address book: #{address}")
            end
            break
          end
        }
        #---------------------------------------------------------------------------------
        # ... sonst in den persoenlichen Kontakten
        #---------------------------------------------------------------------------------
        if(! locallyFound)
          searchPalContact(pattern)
        end
        #---------------------------------------------------------------------------------
        if($foundMailAddresses.length() > 0)
          $foundMailAddresses.each{ |address|
            if(useryes?("#{address} uebernehmen ?"))
              if(useryes?("Als Cc ?"))
                mail.Recipients.add(address).Type=OutlookConst::OlCC
                $client.puts("Added #{address} as cc-recipient")
              else
                mail.Recipients.add(address)
                $client.puts("Added #{address} as recipient")
              end
            end
          }
        else
          $client.puts("No mail-address found")
        end
      #------------------------------------------------------------------------------------
      # Cc-Empfaenger hinzufuegen
      #------------------------------------------------------------------------------------
      elsif(line.match(/cc_(\S+)/))
        line=$1
        mail.Recipients.add(line).Type=OutlookConst::OlCC
        $client.puts("Added #{line} as cc-recipient")
      #------------------------------------------------------------------------------------
      # To-Empfaenger hinzufuegen
      #------------------------------------------------------------------------------------
      elsif(! line.empty?)
        mail.Recipients.add(line)
        $client.puts("Added #{line} as recipient")
      else
        $client.puts("Bitte Befehl eingeben ... #{commands}")
      end
    }
  rescue => exception
    $client.puts("chooseRecipients: Problem #{exception}")
  end
end#}}}}
def createContact(fullName,email1Address)#{{{{
  begin
    contact=$outlook.CreateItem(OutlookConst::OlContactItem)
    contact.fullName=fullName
    contact.email1Address=email1Address
    contact.Save()
    $client.puts("Als Kontakt angelegt: #{fullName} #{email1Address}")
  rescue => exception
    $client.puts("createContact: Problem #{exception}")
  end
end#}}}}
def createCalendar(subject,date,time,duration)#{{{{
  begin
    if(! date.match(/^(\d{4})-(\d{2})-(\d{2})/))
      $client.puts("Ungueltige Datumsangabe #{date}")   
    end
    appDate=$2.to_s+"/"+$3.to_s+"/"+$1.to_s
    appTime=time
    appointment=$outlook.CreateItem(OutlookConst::OlAppointmentItem)
    appointment.Subject=subject
    appointment.Start=appDate + " " + appTime
    appointment.Duration=duration
    if(useryes?("Meetingeinladung ?"))
      appointment.MeetingStatus=OutlookConst::OlMeeting
      appointment.Recipients.add("heinz.breinlinger@firma.de")                        # Cave: Man selbst ist nicht automatisch eingeladen => die Einladung wuerde nicht versandt ;-)
      chooseRecipientsInvitees(appointment,"appointment")
      if useryes?("Einladung #{subject}/#{date}/#{time}/#{duration} senden ?")
        appointment.Save
        appointment.Send
        $client.puts("invitation sent ...")
      else
        $client.puts("invitation cancelled ...")
        return
      end
    else
      $client.puts("Termin #{subject}/#{date}/#{time}/#{duration} gespeichert")
      appointment.Save
    end
  rescue => exception
    $client.puts("createCalendar: Problem #{exception}")
  end
end#}}}}
def deleteCalendar(index)#{{{{
  begin
    index=index.to_i
    if($appointmentList[index])
      appointment=$appointmentList[index]
      $client.printf("%3d) %s to be deleted\n",index,appointment.Subject)
      appointment.Delete()
    else
      $client.puts("Eintrag mit Index #{index} existiert nicht")
    end
  rescue => exception
    $client.puts("deleteCalendar: Problem #{exception}")
  end
end#}}}}
def deleteMail(index)#{{{{
  begin
    index=index.to_i
    if($mailList[index])
      mail=$mailList[index]
      $client.printf("%3d) %20.20s / %20s => %s\n",index,mail.SenderName,mail.Subject,$bin.name)
      mail.Move($bin)
      $mailList=""   
      getMailList()
    else
      $client.puts("Mail mit Index #{index} existiert nicht")
    end
  rescue => exception
    $client.puts("deleteMail: Problem #{exception}")
  end
end#}}}}
def environment()#{{{{
  begin
  rescue => exception
    $client.puts("environment: Problem #{exception}")
  end
end#}}}}
def getMailList()#{{{{
  begin
    $mailList=Array.new
    myItems=$currentFolder.Items
    myItems.sort("[ReceivedTime]",false)     # Aufsteigende Sortierung
    myItems.each{ |mail|
      $mailList.append(mail)
    }
  rescue => exception
    $client.puts("getMailList: Problem #{exception}")
  end
end#}}}}
def help(topic)#{{{{
  begin
    if(! File.file?($helpFile))
      $client.puts("help: Hilfedatei #{$helpFile} existiert nicht")
    else
      #----------------------------------------------------------------------------
      # Seitenweise Ausgabe ?
      #----------------------------------------------------------------------------
      if(topic=="")
        File.open($helpFile,"r"){ |infile|
          i=0
          while(line=infile.gets)
            if(i < $maxList)
              line=line.chop
              $client.puts(line)
              i+=1
            else
              if(useryes?("weiter ?"))
                i=0
              else
                break
              end
            end
          end
        }
      #----------------------------------------------------------------------------
      # Ausgabe eines jeden gefundenen Topic
      #----------------------------------------------------------------------------
      else
        toPrint=false
        File.open($helpFile,"r"){ |infile|
          while(line=infile.gets)
            #----------------------------------------------------------------------
            # Topic gefunden ?
            #----------------------------------------------------------------------
            if(line.match(/^\# #{topic}/)) 
              toPrint=true;              
            end
            #----------------------------------------------------------------------
            # Innerhalb des Ausgabebereiches ?
            #----------------------------------------------------------------------
            if(toPrint)
              if(line.match(/^-/))         # Trennlinie
                toPrint=false;
              else
                line=line.chop
                $client.puts(line)
              end
            end
          end
        }
      end
    end 
  rescue => exception
    $client.puts("help: Problem #{exception}")
  end
end#}}}}
def killmyself()#{{{{
  begin
    $client.puts("Moriturus te salutat")
    $client.close
    $server.close
    system("taskkill /F /PID #{$$} 2>&1")
  rescue => exception
    $client.puts("killmyself: Problem #{exception}")
  end
end#}}}}
def less(mail,index)#{{{{
  begin
    #--------------------------------------------------------------------------
    # Leerzeilen entfernen ?
    #--------------------------------------------------------------------------
    if($compact)
      lineArr=Array.new
      dummyArr=mail.body.split(/\n/)
      dummyArr.each{ |line|
        if(! line.match(/^[\s]*$/))   # Leerzeile
          lineArr.append(line)
        end
      }
    else
      lineArr=mail.body.split(/\n/)
    end
    numLines=lineArr.length()
    lastPossibleLine=numLines-1
    numPages=(numLines >= $maxList) ? numLines/$maxList +1 : 1
    if(mail.Class == OutlookConst::OlMail)
      numAttachments=mail.Attachments.Count
    else
      numAttachments=0
    end
    #--------------------------------------------------------------------------
    $client.puts("#{$lineHash}")
    $client.puts("A N F A N G\n")
    $client.printf("%20.20s: %20s\n",mail.SenderName,mail.Subject)
    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    if(mail.Class == OutlookConst::OlMail)
      $client.puts("#{$lineDots}")
      $client.printf("To: %s\n",mail.To)
      #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
      $client.puts("#{$lineDots}")
      $client.printf("Cc: %s\n",mail.Cc)
    elsif(mail.Class == OutlookConst::OlMeetingCancellation ||
          mail.Class == OutlookConst::OlMeetingForwardNotification ||
          mail.Class == OutlookConst::OlMeetingRequest ||
          mail.Class == OutlookConst::OlMeetingResponseNegative ||
          mail.Class == OutlookConst::OlMeetingResponsePositive ||
          mail.Class == OutlookConst::OlMeetingResponseTentative 
         )
      reminderTime=mail.ReminderTime.to_s.strip
      reminderTime.match(/^(\d{4}-\d{2}-\d{2})/)
      reminderDate=Date.parse($1.to_s)
      weekDay=reminderDate.strftime("%a")
      $client.puts("#{$lineDots}")
      $client.printf("Topic: %s\n",mail.ConversationTopic)
      $client.printf("Time:  %3.3s: %s\n",weekDay,reminderTime)
      $client.printf("Recipients\n")
      mail.Recipients.each{ |recipient|
        $client.printf("   %s\n",recipient.Name)
      }
    end
    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    if(numAttachments >= 1)
      $client.puts("#{$lineDots}")
      mail.Attachments.each{ |attachment|
        $client.printf("Attachment: %3d: %s\n",attachment.Index,attachment.Filename)
      }
    end
    #--------------------------------------------------------------------------
    # Mail-Body anzeigen
    #--------------------------------------------------------------------------
    page=0
    startLine=0
    while(startLine <= lastPossibleLine)
      page=page+1
      out("page #{page} (#{numPages})")
      endLine=startLine+$maxList-1
      endLine=(endLine <= lastPossibleLine) ? endLine : lastPossibleLine
      currentLine=startLine
      while currentLine <= endLine
        $client.puts(lineArr[currentLine])
        currentLine=currentLine+1
      end
      startLine=endLine+1
      #-------------------------------------------------------------------------
      # Letzte Seite noch nicht erreicht
      #-------------------------------------------------------------------------
      if(startLine <= lastPossibleLine)
        out("---- n: stop, b(ack), reply|replyall|forward, del(ete), mv, <key>: scroll forward ------")
        command=$client.gets.chop
        #-----------------------------------------------------------------------
        # Abbruch ?
        #-----------------------------------------------------------------------
        if(command == 'n')
          out("Abbruch")
          break
        #-----------------------------------------------------------------------
        # Loeschen ?
        #-----------------------------------------------------------------------
        elsif(command == 'del')
          deleteMail(index)
          break
        #-----------------------------------------------------------------------
        # Verschieben ?
        #-----------------------------------------------------------------------
        elsif(command == 'mv')
          moveMail(index)
          break
        #-----------------------------------------------------------------------
        # Seite zurueck ?
        #-----------------------------------------------------------------------
        elsif(command == 'b')
          startLine=startLine-$maxList*2  # vor die gerade ausgegebene Seite springen
          if(startLine <=0)
            startLine=0
            page=0
          else
            page=(page > 1) ? page-2 : 0
          end
        #-----------------------------------------------------------------------
        # Antworten oder Weiterleiten ?
        #-----------------------------------------------------------------------
        elsif(command.match(/^(reply|replyall|forward)$/))
          sendMail(command,index.to_i)
        end
      #-------------------------------------------------------------------------
      # Ende der letzten Seite erreicht
      #-------------------------------------------------------------------------
      else
        out("---- n: stop, b(ack), reply|replyall|forward, del(ete), mv ------")
        command=$client.gets.chop
        #-----------------------------------------------------------------------
        # Zurueck zu Listenansicht ?
        #-----------------------------------------------------------------------
        if(command == 'n')
          out("\nE N D E\n")
          break
        #-----------------------------------------------------------------------
        # Loeschen ?
        #-----------------------------------------------------------------------
        elsif(command == 'del')
          deleteMail(index)
          break
        #-----------------------------------------------------------------------
        # Verschieben ?
        #-----------------------------------------------------------------------
        elsif(command == 'mv')
          moveMail(index)
          break
        #-----------------------------------------------------------------------
        # Antworten oder Weiterleiten ?
        #-----------------------------------------------------------------------
        elsif(command.match(/^(reply|replyall|forward)$/))
          sendMail(command,index.to_i)
        end
      end
    end
    #---------------------------------------------------------------------------
    # Nach Mail/Abbruch Liste erneut anzeigen
    #---------------------------------------------------------------------------
    listMails()
  rescue => exception
    $client.puts("less: Problem #{exception}")
  end
end#}}}}
def listCalendar(first,last)#{{{{
  begin
    i=0
    #-------------------------------------------------------------------------------
    # 2022-08-02 => 08/02/2022
    #-------------------------------------------------------------------------------
    first.match(/^(\d{4})-(\d{2})-(\d{2})/)
    first=$2.to_s+"/"+$3.to_s+"/"+$1.to_s
    last.match(/^(\d{4})-(\d{2})-(\d{2})/)
    last=$2.to_s+"/"+$3.to_s+"/"+$1.to_s
    out("Period: #{first} - #{last}","mvcal <Zahl> <yyyy-mm-dd <hh:mm> <duration>")
    #-------------------------------------------------------------------------------
    restriction="[START] >= \"#{first}   00:01 AMPM\" AND [START] <= \"#{last}     23:59 AMPM\""
    items=$calendar.items
    items.IncludeRecurrences=true
    items.Sort("[Start]")
    itemsInRange=items.Restrict(restriction)
    oldAppDate="dummy"
    $appointmentList=Array.new()
    itemsInRange.each{ |appointment|
      $appointmentList.append(appointment)
      appTime=appointment.Start.to_s
      appTime.match(/^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})/)
      appDate=$1.to_s
      appTime=$2.to_s
      appTime=(appTime == "00:00") ? "" : appTime
      weekDay=Date.parse(appDate).strftime("%a")
      duration=appointment.Duration.to_s
      duration=(duration == "1440") ? "day" : duration
      #-------------------------------------------------------------------------------
      # Trennzeile bei Datumswechsel
      #-------------------------------------------------------------------------------
      if(appDate != oldAppDate)
        $client.puts("--------------------------------------------------------------")
        oldAppDate=appDate
      end
      #-------------------------------------------------------------------------------
      $client.printf("%3d) %10.10s (%3.3s) %5.5s (%3.3s) %s\n",i,appDate,weekDay,appTime,duration,appointment.Subject)
      i=i+1
    }
  rescue => exception
    $client.puts("listCalendar: Problem #{exception}")
  end
end#}}}}
def listMails()#{{{{
  begin
    numElements=$mailList.count
    i=0
    $mailList.each{ |mail|
      numAttachments=mail.Attachments.Count
      if(mail.Class == OutlookConst::OlMail)
        # Klammerausdruecke und Rand-Whitespace entfernen
        to=mail.To.gsub(/\([^\)]+\)/,"").strip
        senderName=mail.Sendername.gsub(/\([^\)]+\)/,"").strip
        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        receivedTime=mail.ReceivedTime.to_s.strip
        receivedTime.match(/^(\d{4}-\d{2}-\d{2})/)
        receivedDate=Date.parse($1.to_s)
        weekDay=receivedDate.strftime("%a")
        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        $client.printf("%3d) (%2d) %3.3s %20.20s: %20.20s => %40.40s|%70.70s\n",i,numAttachments,weekDay,receivedTime,senderName,to,mail.Subject)
        if($showAttachmentsInList)
          mail.Attachments.each{ |attachment|
            $client.printf("          Attachment: %3d: %s\n",attachment.Index,attachment.FileName)
          }
          $client.puts("#{$lineDots}")
        end
      elsif(mail.Class == OutlookConst::OlMeetingCancellation ||
            mail.Class == OutlookConst::OlMeetingForwardNotification ||
            mail.Class == OutlookConst::OlMeetingRequest ||
            mail.Class == OutlookConst::OlMeetingResponseNegative ||
            mail.Class == OutlookConst::OlMeetingResponsePositive ||
            mail.Class == OutlookConst::OlMeetingResponseTentative 
           )
        senderName=mail.Sendername.gsub(/\([^\)]+\)/,"").strip
        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        receivedTime=mail.ReceivedTime.to_s.strip
        receivedTime.match(/^(\d{4}-\d{2}-\d{2})/)
        receivedDate=Date.parse($1.to_s)
        weekDay=receivedDate.strftime("%a")
        #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        $client.printf("%3d) (%2d) %3.3s %20.20s: %20.20s => %40.40s|%70.70s\n",i,numAttachments,weekDay,receivedTime,senderName,"MEETING-REQUEST",mail.Subject)
      else
        $client.printf("          Objekt: %s\n",mail.Class)
      end
      #---------------------------------------------------------------------------------------------------
      # Abbruch der Ausgabe ?
      #---------------------------------------------------------------------------------------------------
      if((i > 0) && (i % $maxList == 0))
        out("----Press \"n\" for stop------")
        if($client.gets.chop == 'n')
          $client.puts("Client-Abbruch")
          break
        end
      end
      i=i+1
    }
  rescue => exception
    $client.puts("listMail: Problem #{exception}")
  end
end#}}}}
def listNotes()#{{{{
  begin
    out("Notes")
    items=$notes.items
    #------------------------------------------------------------------------------------------
    # Export und Ausgabe
    #------------------------------------------------------------------------------------------
    File.open("C:/Mails/Notes.txt","w"){ |outfile|
      items.each{ |note|
        $client.printf("%s\n",note.Subject)
        # $client.printf("%s\n",note.Body)
        $client.puts("---------------------------------------------------------------")
        outfile.puts("---------------------------------------------------------------")
        outfile.puts(note.Body)
      }
    }
  rescue => exception
    $client.puts("listNotes: Problem #{exception}")
  end
end#}}}}
def listTasks()#{{{{
  begin
    out("Tasks")
    items=$tasks.items
    items.each{ |task|
      $client.printf("%s\n",task.Subject)
      $client.printf("%s\n",task.Body)
      $client.puts("---------------------------------------------------------------")
    }
  rescue => exception
    $client.puts("listTasks: Problem #{exception}")
  end
end#}}}}
def moveCalendar(index,toDate,toTime,duration)#{{{{
  begin
    #-------------------------------------------------------------------------------
    index=index.to_i
    if($appointmentList.length() < index)
      $client.puts("Index #{index} ist nicht in Eintragsliste vorhanden (Laenge: #{$appointmentList.length()}")
      return
    end
    if(! toDate.match(/^(\d{4})-(\d{2})-(\d{2})/))
      $client.puts("Ungueltige Datumsangabe #{toDate}")   
    end
    appDate=$2.to_s+"/"+$3.to_s+"/"+$1.to_s
    appTime=toTime;
    appointment=$appointmentList[index]
    $client.puts("=> Verschiebe Eintrag #{index} auf #{toDate} um #{toTime} mit Dauer #{duration}")
    appointment.Start=appDate + " " + appTime
    appointment.Duration=duration;
    appointment.Save
  rescue => exception
    $client.puts("moveCalendar: Problem #{exception}")
  end
end#}}}}
def moveMail(index)#{{{{
  begin
    index=index.to_i
    if($mailList[index])
      mail=$mailList[index]
      $client.printf("%3d) %20.20s / %20s => %s\n",index,mail.SenderName,mail.Subject,$targetFolder.name)
      mail.Move($targetFolder)
      $mailList=""   
      getMailList()
    else
      $client.puts("Mail mit Index #{index} existiert nicht")
    end
  rescue => exception
    $client.puts("moveMail: Problem #{exception}")
  end
end#}}}}
def out(*messages)#{{{{
  begin
    $client.puts($lineHash)
    messages.each{ |message|
      if(! message.empty?)
        $client.puts(message)
      end
    }
    $client.puts($lineHash)
  rescue => exception
    $client.puts("message: Problem #{exception}")
  end
end#}}}}
def prompt()#{{{{
  begin
    $client.puts($lineHash)
    $client.printf("%20.20s |%20.20s |%10.10s |%10.10s |%10.10s |%10.10s\n","Aktuell","Ziel","Zeilen","Wochen","Kompakt","Anhaenge");
    $client.printf("%20.20s |%20.20s |%10.20s |%10.20s |%10.10s |%10.10s\n",$currentFolder.Name,$targetFolder.Name,$maxList,$weeks,$compact ? "Ja" : "Nein",$showAttachmentsInList ? "Ja" : "Nein");
    $client.puts($lineHash)
  rescue => exception
    $client.puts("prompt: Problem #{exception}")
  end
end#}}}}
def loadLocalAddressbook()#{{{{
  begin
    $localAddressHash=Hash.new()
    getHashFromFile($localAddressHash,$localAddressBook)
  rescue => exception
    $client.puts("loadLocalAddressbook: Problem #{exception}")
  end
end#}}}}
def searchPalContact(pattern)#{{{{
  # pattern ist der Name des zu findenden Kollegen in der Personal Address List (vulgo: Kontakte)
  begin
    $client.puts("Searching for |#{pattern}| ...")
    # hitList=$contacts.Items.restrict("[LastName]=\"#{pattern}\"")
    hitList=$contacts.Items.restrict("[LastName]=\"#{pattern}\" OR [FirstName]=\"#{pattern}\"")
    numHits=hitList.Count
    out("#{numHits} Treffer")
    if(numHits > 0)
      $foundMailAddresses=Array.new()
      hitList.each{ |contact|
        firstName=contact.firstName
        lastName=contact.LastName
        fullName=contact.FullName
        businessTelephoneNumber=contact.businessTelephoneNumber
        mobileTelephoneNumber=contact.MobileTelephoneNumber
        email1Address=contact.email1Address
        mailingAddress=contact.mailingAddress
        companyName=contact.companyName
        if(! email1Address.empty?)
          $foundMailAddresses.append(email1Address)
        elsif(! mailingAddress.empty?)
          $foundMailAddresses.append(mailingAddress)
        end
        out(firstName+" "+lastName+" ("+fullName+")",companyName,businessTelephoneNumber,mobileTelephoneNumber,email1Address,mailingAddress)
      }
    end
  rescue => exception
    $client.puts("searchPalContact: Problem #{exception}")
  end
end#}}}}
def searchGalContact(pattern)#{{{{
  # pattern ist der Name des zu findenden Kollegen
  begin
    $client.puts("Searching for |#{pattern}| ...")
    gal=$outlook.Session.GetGlobalAddressList()
    entry=gal.AddressEntries(pattern)
    # name=entry.Name
    # entry.Details()  # => Popup-Box mit allen Kontaktinformationen
    if(entry.AddressEntryUserType == OutlookConst::OlExchangeUserAddressEntry ||
       entry.AddressEntryUserType == OutlookConst::OlExchangeRemoteUserAddressEntry
      )
      contact=entry.getExchangeUser
      $foundSmtpAddress=smtpAddress=contact.PrimarySmtpAddress
      bensl=contact.Alias
      lastName=contact.LastName
      firstName=contact.FirstName
      businessPhone=contact.BusinessTelephoneNumber
      mobilePhone=contact.MobileTelephoneNumber
      department=contact.Department
      officeLocation=contact.OfficeLocation
      streetAddress=contact.StreetAddress
      postalCode=contact.PostalCode
      city=contact.City
      #---------------------------------------------------------------------------------------------------------------------------------------------------------
      # Cave: entry ist hier "nur" ein AddressEntry-Objekt, nicht wie contact ein von AddressEntry abgeleiteter ExchangeUser (mit anderen Property-Feldern),
      #       Schemas koennten bei beiden abgefragt werden
      #          bei AddressEntry des Typs http://schemas.microsoft.com/mapi/proptag/0x800F101F
      #          bei ExchangeUser des Typs http://schemas.microsoft.com/mapi/id/{00062004-0000-0000-C000-000000000046}/8076001f
      #       cf. hierzu auch Anleitung unter .../Outlook
      #---------------------------------------------------------------------------------------------------------------------------------------------------------
      # mailProperty=entry.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x800F101F")   # $client.puts("#{name} => #{mailProperty}")
      # mobile=entry.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3a1c001f")
      # mailAddress=/"SMTP:([^"]+)"/.match(mailProperty.to_s)[1]                  # mailAddress=mailProperty.to_s[/"SMTP:([^"]+)"/,1]  funktioniert ebenfalls
      #---------------------------------------------------------------------------------------------------------------------------------------------------------
      out(firstName+" "+lastName,bensl,department,officeLocation,streetAddress,postalCode,city,smtpAddress,businessPhone,mobilePhone)
    else
      $client.puts("Could not find #{pattern}")
    end
=begin
    galEntries=gal.AddressEntries()
    i=0
    galEntries.each{ |entry|
      i=i+1
      if(i > 10)
        break
      end
      user=entry.GetExchangeUser
      $client.puts("Entry: #{entry.Address}, #{entry.Name}")
    }
=end
  rescue => exception
    $client.puts("searchGalContact: Problem #{exception}")
  end
end#}}}}
def searchMails(pattern)#{{{{
  #---------------------------------------------------------------------
  #  sFilter = "@SQL=""urn:schemas:httpmail:datereceived"" > '12/15/2025 07:30:00'"
  #  sFilter = sFilter & " AND "
  #  sFilter = sFilter & """urn:schemas:httpmail:hasattachment"" = 1" (0 = kein Attachment)
  #  "@SQL=""urn:schemas:httpmail:fromemail"" ci_phrasematch 'xxx@text.com'"
  #  There’s also the ci_startswith keyword that may be useful in certain scenarios.
  #  
  # search="@SQL=\"urn:schemas:httpmail:subject\" like '%Fieldglass%'"  # % ist Wildcard (quasi '*')
  # search="@SQL=\"urn:schemas:httpmail:fromemail\" ci_phrasematch 'robert.mustermann@firma.de'"
  # search="@SQL=\"urn:schemas:httpmail:fromemail\" like '%nicke%'"
  # search="@SQL=\"urn:schemas:httpmail:datereceived\" > '2022-07-29 23:59:59'"
  # search="@SQL=\"urn:schemas:httpmail:datereceived\" >= '2022-08-02 00:00:01'"
  # search="@SQL=\"urn:schemas:httpmail:displaycc\" like '%temp%'"
  # search="@SQL=\"urn:schemas:httpmail:displayto\" like '%aenicke%'"
  # search="@SQL=\"urn:schemas:httpmail:displayto\" like '%#{pattern}%'"
  #---------------------------------------------------------------------
  begin
    search=""
    dummyArr=pattern.split(":")
    if(dummyArr.length() != 2)
      $client.puts("Ungueltiger Pattern-Wert: #{pattern}")
    else 
      action=dummyArr[0]
      token=dummyArr[1]
      out("Search: #{action}   Token: #{token}")
      case action
        when "from"
          search="@SQL=\"urn:schemas:httpmail:fromname\" like '%#{token}%'"
        when "to"
          search="@SQL=\"urn:schemas:httpmail:displayto\" like '%#{token}%'"
        when "cc"
          search="@SQL=\"urn:schemas:httpmail:displaycc\" like '%#{token}%'"
        when "subject"
          search="@SQL=\"urn:schemas:httpmail:subject\" like '%#{token}%'"
        when "before"
          search="@SQL=\"urn:schemas:httpmail:datereceived\" < '#{token} 00:00:01'"
        when "after"
          search="@SQL=\"urn:schemas:httpmail:datereceived\" > '#{token} 23:59:59'"
        else
          $client.puts("Ungueltige Aktion: #{action}")
        end
      if(! search.empty? )
        hitList=$currentFolder.Items.Restrict(search)
        numHits=hitList.Count
        out("#{numHits} Treffer")
        if(numHits > 0)
          $mailList=Array.new
          hitList.each{ |mail|
            $mailList.append(mail)
          }
          listMails()
        end
      end
    end
  rescue => exception
    $client.puts("searchMails: Problem #{exception}")
  end
end#}}}}
def sendMail(type="new",index=-1)#{{{{
  begin
    case type
      when "new"
        mail=$outlook.CreateItem(OutlookConst::OlMailItem)
        #-------------------------------------------------------------------------------
        # Subject
        #-------------------------------------------------------------------------------
        out("Subject eingeben ... ")
        mail.Subject=$client.gets.chop
        chooseRecipientsInvitees(mail,"mail")
        text=writeText
        mail.Body=text
      when "forward", "reply", "replyall"
          if(index < 0)
            $client.puts("sendMail: Index < 0 => kein Index uebergeben")
          elsif($mailList[index])
            mail=$mailList[index]
            if(type == "forward")
              mail=mail.Forward
            elsif(type == "reply")
              mail=mail.Reply
            elsif(type == "replyall")
              mail=mail.ReplyAll
            else
              $client.puts("sendMail: Internal error forward/reply")
            end
            chooseRecipientsInvitees(mail,"mail")
            text=writeText
            mail.Body=text+"\n\n-------------------------------------------------------------------\n\n"+mail.Body
          else
            $client.puts("sendMail: Mail mit Index #{index} existiert nicht")
          end
      else
        $client.puts("sendMail: Unbekannte Typ: #{type}")
      end
    # mail.Display
    attach(mail)
    if(useryes?("Mail versenden ?"))
      mail.Send
      out("mail sent !")
    else
      out("Abbruch")
    end
  rescue => exception
    $client.puts("sendMail: Problem #{exception}")
  end
end#}}}}
def showMailDir()#{{{{
  #-------------------------------------------------------------------------
  # Anzeige aller Dateien des Mail-Verzeichnisses
  #-------------------------------------------------------------------------
  begin
    Dir.chdir($mailDir)
    $client.puts($lineHash)
    $client.puts("Verzeichnisinhalt")
    Dir.glob(["*.*"]).each{ |file|
      $client.puts("#{$mailDir}: #{file}")
    }
    $client.puts($lineHash)
  rescue => exception
    $client.puts("showMailDir: Problem #{exception}")
  end
end#}}}}
def writeText()#{{{{
  begin
    #-------------------------------------------------------------------------------
    # Text
    #-------------------------------------------------------------------------------
    text=""
    out("Text eingeben ... (done,clear,show)")
    loop{
      line=$client.gets.chop # Newline abschneiden
      if(line == "done")
        break
      elsif(line == "clear")
        text=""
        out("cleared")
      elsif(line == "show")
        out(text) 
      else
        text=text + "\n" + line
      end
    }
    return text
  rescue => exception
    $client.puts("writeText: Problem #{exception}")
  end
end#}}}}
def useryes?(question)#{{{{
  begin
    $client.puts("#{question} [<Taste>/n] ?")
    if($client.gets.chop == 'n')
      return false
    else
      return true
    end
  rescue => exception
    $client.puts("useryes: Problem #{exception}")
  end
end#}}}}
#{{{{main

loadLocalAddressbook()
startOutlook
$currentFolder=$topFolder
$targetFolder=$done

$server=TCPServer.new("192.168.224.1",4444)     # IP-Adresse von Windows auf virtueller WSL-Schnittstelle (cf. ipconfig)
loop{
  $client=$server.accept
  $client.puts("connected")
=begin
  $localAddressHash.each{ |name,address|
    $client.puts("#{name} => #{address}")
  }
=end
  loop{
    begin
      prompt
      request=$client.gets
      #{{{{ Verzeichniswechsel
      #---------------------------------------------------------------------------------------
      # Verzeichniswechsel
      #---------------------------------------------------------------------------------------
      if(request.match(/^cd\s+"([^"]+)"/i) || request.match(/^cd\s+([[:alnum:]]+)/i))
        wantedFolder=$1;
        found=false
        $client.puts("Wanted: |#{wantedFolder}| ...")
        $currentFolder.Folders.each{ |folder|
          if(folder.name.match(/#{wantedFolder}/i))
            $currentFolder=folder
            found=true
            break
          end
        }
        if(found)
          $client.puts("Changed to: #{$currentFolder.name}")
        else
          $client.puts("No Folder #{wantedFolder} found in #{$currentFolder.name}")
        end
      #}}}}
      #{{{{ Adress-Buch neu laden 
      #---------------------------------------------------------------------------------------
      # Adress-Buch einlesen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^reload/i))
        loadLocalAddressbook()
        $client.puts("local address book reloaded")
      #}}}}
      #{{{{ Shortcuts
      #---------------------------------------------------------------------------------------
      # Short-cuts
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^cd([[:alpha:]]+)/i))
        shortcut=$1
        case shortcut
          when "top"
            $currentFolder=$topFolder
          when "in"
            $currentFolder=$inbox
          when "out"
            $currentFolder=$outbox
          when "sent"
            $currentFolder=$sent
          when "bin"
            $currentFolder=$bin
          when "done"
            $currentFolder=$done
          when "cc"
            $currentFolder=$cc
          when "field"
            $currentFolder=$fieldglass
          else
            $client.puts("Unknown shortcut: #{shortcut} (in,out,bin,done,cc,field)")
          end
        $client.puts("Current Folder: #{$currentFolder.name}")
      #}}}}
      #{{{{ Zielordner
      #---------------------------------------------------------------------------------------
      # Festlegung eines Zielordners fuer Folgeoperationen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^target\s+"([^"]+)"/i) || request.match(/^target\s+([[:alnum:]]+)/i))
        wantedFolder=$1;
        found=false
        $client.puts("Wanted: |#{wantedFolder}| ...")
        $currentFolder.Folders.each{ |folder|
          if(folder.name.match(/#{wantedFolder}/i))
            $targetFolder=folder
            found=true
            break
          end
        }
        if(found)
          $client.puts("New Target: #{$targetFolder.name}")
        else
          $client.puts("No Folder #{wantedFolder} found in #{$currentFolder.name}")
        end
      #}}}}
      #{{{{ Anzeige aller Elemente
      #---------------------------------------------------------------------------------------
      # Anzeige (Ordner, Mails), Numerierung und Vorhalten aller Mails
      # Cave: Sortierung funktioniert NICHT direkt mit
      #         $currentFolder.Items.sort(....)
      #       Vorherige Zuweisung
      #         myItems=$currentFolder.Items 
      #       ist zwingend
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^ls/i))
        $currentFolder.Folders.each{ |folder|
          $client.puts("#{folder.name}")
        }
        $client.puts($lineHash)
        getMailList()
        listMails()
      #}}}}
      #{{{{ Loeschen von Kalendereintraegen
      #---------------------------------------------------------------------------------------
      # Loeschen von Mails
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^delcal\s+(\d+)/))
        index=$1
        deleteCalendar(index)
      #}}}}
      #{{{{ Loeschen von Mails
      #---------------------------------------------------------------------------------------
      # Loeschen von Mails
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^del\s+(\d+)/))
        index=$1
        if($currentFolder.name == $bin.name)
          $client.puts("Wir sind bereits im Papierkorb ;-)")
        elsif($mailList.empty?)
          $client.puts("Mail-Liste ist leer")
        else
          deleteMail(index)
        end
      #}}}}
      #{{{{ Verschieben von Mails
      #---------------------------------------------------------------------------------------
      # Verschieben von Mails
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^mv\s+(\d+)/))
        index=$1
        if($currentFolder.name == $targetFolder.name)
          $client.puts("Quell- und Zielordner sind identisch")
        elsif($mailList.empty?)
          $client.puts("Mail-Liste ist leer")
        else
          moveMail(index)
        end
      #}}}}
      #{{{{ Absender => Kontakt anlegen
      #---------------------------------------------------------------------------------------
      # Absender in Kontakte uebernehmen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^savecontact\s+(\d+)/))
        index=$1
        index=index.to_i
        if($mailList[index])
          mail=$mailList[index]
          #-----------------------------------------------------------------------------------
          # Cave: In Feld senderEmailAddress wird die STMP-Adresse im Exchange-internen
          # X500-Format
          #   /O=EXCHANGELABS/OU=EXCHANGE ADMINISTRATIVE GROUP
          # gespeichert, woraufhin alle (!) Kontaktdaten vom Server gezogen werden, sobald
          # der neu angelegte Kontakt erstmalig im GUI angeklickt wurde. Leider wird dabei
          # zwar die Mail-Adresse richtig dargestellt/konvertiert; im Feld verbleibt
          # jedoch der x500-String 
          #-----------------------------------------------------------------------------------
          senderName=mail.Sendername.gsub(/\([^\)]+\)/,"").strip
          senderEmailAddress=mail.senderEmailAddress
          createContact(senderName,senderEmailAddress)
        else
          $client.puts("Mail mit Index #{index} existiert nicht")
        end
      #}}}}
      #{{{{ Mail als MSG,TXT oder PDF speichern
      #---------------------------------------------------------------------------------------
      # Mail als PDF, Text oder Mailformat speichern
      #   save 6 Hugo.txt|pdf|msg
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^save\s+(\d+)\s+(\S+)\.(pdf|txt|msg)/))
        begin
          if(! File.directory?($mailDir))
            $client.puts("Mail-Verzeichnis #{$mailDir} existiert nicht")
          else
            index=$1
            fileName=$2
            ending=$3
            if($mailList.empty?)
              $client.puts("Mail-Liste ist leer")
            else
              index=index.to_i
              if($mailList[index])
                mail=$mailList[index]
                if(ending == "pdf")
                  fileName=$mailDirWin+"\\"+fileName+"."+ending
                else 
                  fileName=$mailDir+"/"+fileName+"."+ending
                end
                $client.puts("Saving #{fileName} ...")
                case ending
                  when "pdf"
                    word=mail.GetInspector.WordEditor
                    word.ExportAsFixedFormat(fileName,17)  # 17 == wdExportFormatPDF
                  when "msg"
                    mail.SaveAs(fileName,OutlookConst::OlMSG)
                  when "txt"
                    mail.SaveAs(fileName,OutlookConst::OlTXT)
                  else
                    $client.puts("Unkknown ending #{ending}")
                  end
              else
                $client.puts("Mail mit Index #{index} existiert nicht")
              end
            end
            showMailDir
          end
        rescue => exception
          $client.puts("Attempt to save mail failed: Problem #{exception}")
        end
      #}}}}
      #{{{{ Mailattachments speichern
      #---------------------------------------------------------------------------------------
      # Mail-Attachments speichern
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^saveattach\s+(\d+)/))
        begin
          if(! File.directory?($mailDir))
            $client.puts("Mail-Verzeichnis #{$mailDir} existiert nicht")
          else
            index=$1
            if($mailList.empty?)
              $client.puts("Mail-Liste ist leer")
            else
              index=index.to_i
              if($mailList[index])
                mail=$mailList[index]
                mail.Attachments.each{ |attachment|
                  fileName=$mailDir+"/"+attachment.Filename
                  $client.puts("Saving #{fileName} ...")
                  attachment.SaveAsFile(fileName)
                }
              else
                $client.puts("Mail mit Index #{index} existiert nicht")
              end
            end
          end
        rescue => exception
          $client.puts("Attempt to save attachments failed: Problem #{exception}")
        end
      #}}}}
      #{{{{ Mailinhalt anzeigen
      #---------------------------------------------------------------------------------------
      # Anzeige des Mail-Inhaltes
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^r\s+(\d+)/))
        index=$1
        if($mailList.empty?)
          $client.puts("Mail-Liste ist leer")
        else
          index=index.to_i
          if($mailList[index])
            mail=$mailList[index]
            less(mail,index)
          else
            $client.puts("Mail mit Index #{index} existiert nicht")
          end
        end
      #}}}}
      #{{{{ Anzahl anzuzeigender Mail-Betreffe
      #---------------------------------------------------------------------------------------
      # Anzahl auf einmal auszugebender Mail-Betreffs aendern
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^rows\s+(\d+)/))
        $maxList=$1.to_i
        $client.puts("Neue Anzahl auszugebender Mail-Subjects #{$maxList} Zeichen")
      #}}}}
      #{{{{ Anzahl anzuzeigender Kalenderwochen
      #---------------------------------------------------------------------------------------
      # Anzahl anzuzeigender Kalenderwochen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^weeks\s+(\d+)/))
        $weeks=$1.to_i
        $client.puts("Neue Wochenzahl: #{$weeks}")
      #}}}}
      #{{{{ Kompakte Darstellung ?
      #---------------------------------------------------------------------------------------
      # Kompakte Darstellung ?
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^compact\s+(y|n)/i))
        case $1.to_s
          when "y"
            $compact=true
            $client.puts("=> Kompakte Darstellung ein")
          when "n"
            $compact=false
            $client.puts("=> Kompakte Darstellung aus")
          else
            $client.puts("Ungueltiger Wert #{$1}")
          end
      #}}}}
      #{{{{ Mailanhaenge in Listen darstellen ?
      #---------------------------------------------------------------------------------------
      # Mailanhaenge in Listen darstellen ?
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^showattach\s+(y|n)/i))
        case $1.to_s
          when "y"
            $showAttachmentsInList=true
            $client.puts("=> Listendarstellung inkl. Anhaenge ein")
          when "n"
            $showAttachmentsInList=false
            $client.puts("=> Listendarstellung inkl. Anhaenge aus")
          else
            $client.puts("Ungueltiger Wert #{$1}")
          end
      #}}}}
      #{{{{ Hilfe
      #---------------------------------------------------------------------------------------
      # Hilfe (optional ab einem Topic
      #        Cave: Letzteres muss mit in Hilfe-datei beginnen mit
      #          #<blank>
      #       )
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^help/i))
        if(request.match(/^help\s+(\S+)/))
          help($1.to_s)
        else
          help("")
        end
      #}}}}
      #{{{{ Suche
      #---------------------------------------------------------------------------------------
      # Suche
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^search\s+(\S+)/i))
        searchMails($1)
      #}}}}
      #{{{{ Mail-Versand
      #---------------------------------------------------------------------------------------
      # Mail-Versand
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^mail/i))
        sendMail("new")
      elsif(request.match(/^(forward|reply|replyall)\s+(\d+)/))
        sendMail($1.to_s,$2.to_i)
      #}}}}
      #{{{{ Meeting-Antwort
      #---------------------------------------------------------------------------------------
      # Meeting-Antwort
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^meeting\s+(\d+)\s+(yes|no|perhaps)/))
        index=$1.to_i
        reply=$2.to_s
        case reply
          when "yes"
            responseCode=OutlookConst::OlMeetingAccepted 
          when "perhaps"
            responseCode=OutlookConst::OlMeetingTentative
          when "no"
            responseCode=OutlookConst::OlMeetingDeclined
          else
            $client.puts("Internal Error: Invalid value #{reply}")
          end
        if($mailList[index])
          mail=$mailList[index]
          if(! (mail.Class == OutlookConst::OlMeetingRequest))
            $client.puts("Objekt #{mail.Class} ist keine Meeting-Einladung")
          else
            meetCopy=mail.GetAssociatedAppointment(true)
            meetCopy.Respond(responseCode)
            meetCopy.Send
            $client.puts("Response sent")
          end
        else
          $client.puts("Mail mit Index #{index} existiert nicht")
        end
      #}}}}
      #{{{{ Kill
      #---------------------------------------------------------------------------------------
      # Kill des Servers
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^kill/i))
        killmyself
      #}}}}
      #{{{{ Adressbuch-Suche
      #---------------------------------------------------------------------------------------
      # Adressbuch-Suche (Personal Address List)
      #   Cave: Reihenfolge des or-statements hier wichtig, da das zweite Pattern ebenfalls
      #         ein Muster der Art "Maier, Mario" matchen wuerden
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^pal\s+"([^"]+)"/i) or request.match(/^pal\s+(\S+)/i))
        searchPalContact($1.to_s) 
      #}}}}
      #{{{{ Global Address List
      #---------------------------------------------------------------------------------------
      # Global Address List
      #   Cave: Reihenfolge des or-statements hier wichtig, da das zweite Pattern ebenfalls
      #         ein Muster der Art "Maier, Mario" matchen wuerden
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^gal\s+"([^"]+)"/i) or request.match(/^gal\s+(\S+)/i))
        searchGalContact($1.to_s) 
      #}}}}
      #{{{{ Ausstieg
      #---------------------------------------------------------------------------------------
      # Ausstieg des Client
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^exit/i))
        $client.puts("Goodbye")
        $client.close
        break
      #}}}}
      #{{{{ Anzeige des Mailverzeichnisses
      #---------------------------------------------------------------------------------------
      # Anzeige des Mailverzeichnisses
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^files/i))
        showMailDir
      #}}}}
      #{{{{ Kalender anzeigen
      #---------------------------------------------------------------------------------------
      # Kalender anzeigen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^calendar/))
        if(request.match(/^calendar\s+(\d{4}-\d{2}-\d{2})\s+(\d{4}-\d{2}-\d{2})/i))
          from=$1.to_s
          till=$2.to_s 
        else
          from=Date.today()
          till=from + $weeks*7
          from=from.strftime("%Y-%m-%d")
          till=till.strftime("%Y-%m-%d")
        end
        listCalendar(from,till)
      #}}}}
      #{{{{ Kalendereintrag verschieben
      #---------------------------------------------------------------------------------------
      # zuvor muss $appointmentList durch Anzeige des Kalenders gefuellt worden sein
      # mvcal 17 2025-11-08 07:45 120
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^mvcal\s+(\d+)\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+(\d+)/i))
        index=$1.to_s
        toDate=$2.to_s
        toTime=$3.to_s
        duration=$4.to_s
        moveCalendar(index,toDate,toTime,duration)
      #}}}}
      #{{{{ Kalendereintrag anlegen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^createcal\s+(\S+)\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+(\d+)/i) ||
           (request.match(/^createcal\s+"([^"]+)"\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+(\d+)/i)))    # subject in Gaensefuesschen
        subject=$1.to_s
        date=$2.to_s
        time=$3.to_s
        duration=$4.to_s
        createCalendar(subject,date,time,duration)
      #}}}}
      #{{{{ Tasks anzeigen
      #---------------------------------------------------------------------------------------
      # Tasks anzeigen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^tasks/i))
        listTasks()
      #}}}}
      #{{{{ Notizen anzeigen
      #---------------------------------------------------------------------------------------
      # Notizen anzeigen
      #---------------------------------------------------------------------------------------
      elsif(request.match(/^notes/i))
        listNotes()
      #}}}}
      #{{{{ Default
      #---------------------------------------------------------------------------------------
      # Default
      #---------------------------------------------------------------------------------------
      else
        $client.puts("Client: Request \"#{request.chop}\" nicht implementiert")
      end
      #}}}}
      #---------------------------------------------------------------------------------------
    rescue => exception
      dieClient("main: #{exception}")
    end
  }
  $client.close
}#}}}}
#{{{{ urn:schemas
# ---------------------------------------------------------------------------------------------------------------
# https://docs.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2007/aa579702(v=exchg.80)
# urn:schemas:mailheader:approved
# urn:schemas:httpmail:attachmentfilename
# urn:schemas:mailheader:bcc
# urn:schemas:httpmail:bcc
# urn:schemas:httpmail:cc
# urn:schemas:mailheader:cc
# urn:schemas:mailheader:comment
# urn:schemas:mailheader:content-base
# urn:schemas:mailheader:content-class
# urn:schemas:mailheader:content-description
# urn:schemas:mailheader:content-disposition
# urn:schemas:httpmail:content-disposition-type
# urn:schemas:mailheader:content-id
# urn:schemas:mailheader:content-language
# urn:schemas:mailheader:content-location
# urn:schemas:httpmail:content-media-type
# urn:schemas:mailheader:content-transfer-encoding
# urn:schemas:mailheader:content-type
# urn:schemas:mailheader:control
# urn:schemas:httpmail:date
# urn:schemas:mailheader:date
# urn:schemas:httpmail:datereceived
# urn:schemas:httpmail:displaycc
# urn:schemas:httpmail:displayto
# urn:schemas:mailheader:disposition
# urn:schemas:mailheader:disposition-notification-to
# urn:schemas:mailheader:distribution
# urn:schemas:mailheader:expires
# urn:schemas:mailheader:expiry-date
# urn:schemas:httpmail:flagcompleted
# urn:schemas:mailheader:followup-to
# urn:schemas:httpmail:from
# urn:schemas:mailheader:from
# urn:schemas:httpmail:fromemail
# urn:schemas:httpmail:fromname
# urn:schemas:httpmail:hasattachment
# urn:schemas:httpmail:htmldescription
# urn:schemas:httpmail:importance
# urn:schemas:mailheader:importance
# urn:schemas:mailheader:in-reply-to
# urn:schemas:mailheader:keywords
# urn:schemas:mailheader:lines
# urn:schemas:mailheader:message-id
# urn:schemas:httpmail:messageflag
# urn:schemas:mailheader:mime-version
# urn:schemas:mailheader:newsgroups
# urn:schemas:httpmail:normalizedsubject
# urn:schemas:mailheader:organization
# urn:schemas:mailheader:original-recipient
# urn:schemas:mailheader:path
# urn:schemas:mailheader:posting-version
# urn:schemas:httpmail:priority
# urn:schemas:mailheader:priority
# urn:schemas:mailheader:received
# urn:schemas:mailheader:references
# urn:schemas:mailheader:relay-version
# urn:schemas:httpmail:reply-by
# urn:schemas:mailheader:reply-by
# urn:schemas:httpmail:reply-to
# urn:schemas:mailheader:reply-to
# urn:schemas:mailheader:return-path
# urn:schemas:mailheader:return-receipt-to
# urn:schemas:httpmail:savedestination
# urn:schemas:httpmail:saveinsent
# urn:schemas:mailheader:sender
# urn:schemas:httpmail:sender
# urn:schemas:httpmail:senderemail
# urn:schemas:httpmail:sendername
# https://schemas.microsoft.com/exchange/sensitivity
# urn:schemas:mailheader:sensitivity
# urn:schemas:httpmail:subject
# urn:schemas:mailheader:subject
# urn:schemas:httpmail:submitted
# urn:schemas:mailheader:summary
# urn:schemas:httpmail:textdescription
# urn:schemas:mailheader:thread-index
# urn:schemas:mailheader:thread-topic
# urn:schemas:httpmail:thread-topic
# urn:schemas:httpmail:to
# urn:schemas:mailheader:to
# urn:schemas:mailheader:x-mailer
# urn:schemas:mailheader:x-message-completed
# urn:schemas:mailheader:x-message-flag
# urn:schemas:mailheader:x-unsent
# urn:schemas:mailheader:xref
# ---------------------------------------------------------------------------------------------------------------
#}}}}
#{{{{ OlObjectClass
#olMeetingCancellation  54  A MeetingItem object that is a meeting cancellation notice.
#olMeetingForwardNotification   181   A MeetingItem object that is a notice about forwarding the meeting request.
#olMeetingRequest   53  A MeetingItem object that is a meeting request.
#olMeetingResponseNegative  55  A MeetingItem object that is a refusal of a meeting request.
#olMeetingResponsePositive  56  A MeetingItem object that is an acceptance of a meeting request.
#olMeetingResponseTentative   57  A MeetingItem object that is a tentative acceptance of a meeting request.
#}}}}
