# convert.tcl --
#
# This file contains code which converts old version of the database and
# vfolders to the latest version.
#
#
#  TkRat software and its included text is Copyright 1996,1997,1998
#  by Martin Forssén
#
#  Postilion software and its included text and images
#  Copyright (C) 1998, 1999, 2000 Nic Bernstein
#
#  The full text of the legal notices is contained in the files called
#  COPYING and COPYRIGHT.TkRat, included with this distribution.
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
# 
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


# FixDbase --
#
# Convert the database from the old format to the current
#
# Arguments:

proc FixDbase {} {
    global fix_scale option t

    # Check with user
    if { 0 != [RatDialog $t(upgrade_dbase) $t(old_dbase) {} \
	    0 $t(continue) $t(abort)]} {
	exit 1
    }
    wm withdraw .
    set dir [RatTildeSubst $option(dbase_dir)]
    FixOldDbase dir

    # Tell user what we are doing
    set w .upgradeDbase
    toplevel $w -class Postilion
    wm title $w "Upgrade dbase"
    scale $w.scale -length 6c -showvalue 0 -sliderlength 5 \
	    -variable fix_scale  -orient horiz
    pack $w.scale -side top -padx 5 -pady 5
    Place $w fixDbase

    # Find how many entries we must fix
    set fh [open $dir/index.ver r]
    gets $fh version
    gets $fh entries
    close $fh
    set fix_scale 0
    $w.scale configure -to $entries

    # Do actual fixing
    set lock [open $dir/lock w]
    puts $lock "Updating"
    close $lock
    set newIndex [open $dir/index.new w]
    set oldIndex [open $dir/index r]
    for {set fix_scale 0} {$fix_scale < $entries} {incr fix_scale} {
	update idletasks
	for {set i 0} {$i < 14} {incr i} {
	    gets $oldIndex line($i)
	}
	# To
	set result $line(0)
	regsub {@.+$} $result {} name
	while {[regexp {[a-zA-Z][ 	]+[a-zA-Z]} $result match]} {
	    regsub {[ 	]+} $match {,} subst
	    regsub $match $result $subst result
	}
	puts -nonewline $newIndex $result
	regsub {(, )+} $line(1) {} result
	if [string length $result] {
	    puts -nonewline $newIndex " ($result)"
	}
	puts $newIndex ""
	# From
	set result $line(2)
	while {[regexp {[a-zA-Z][ 	]+[a-zA-Z]} $result match]} {
	    regsub {[ 	]+} $match {,} subst
	    regsub $match $result $subst result
	}
	puts -nonewline $newIndex $result
	regsub {(, )+} $line(3) {} result
	if [string length $result] {
	    puts -nonewline $newIndex " ($result)"
	}
	puts $newIndex ""
	# Cc
	puts $newIndex $line(4)
	# Subject
	puts $newIndex $line(5)
	# Date (UNIX time_t as a string)
	puts $newIndex $line(6)
	# Keywords (SPACE separated list)
	puts $newIndex $line(7)
	# Size
	puts $newIndex [file size $dir/dbase/$line(13)]
	# Status
	set status ""
	set msgFh [open $dir/dbase/$line(13) r]
	while {[string length [gets $msgFh hline]]} {
	    if { 0 == [string length $hline]} {
		break
	    }
	    if ![string compare status: [string tolower [lindex $hline 0]]] {
		set status [lindex $hline 1]
		break
	    }
	}
	close $msgFh
	puts $newIndex $status
	# Expiration time (UNIX time_t as a string)
	if [string length $line(11)] {
	    puts $newIndex [RatTime +100]
	} else {
	    puts $newIndex ""
	}
	# Expiration event (none, remove, incoming, backup or custom *)
	puts $newIndex $line(12)
	# Filename
	regsub {[%,].+} $name {} fdir
	if [file exists $dir/dbase/$fdir/.seq] {
	    set seqFh [open $dir/dbase/$fdir/.seq r+]
	    set sequence [expr 1+[gets $seqFh]]
	    seek $seqFh 0
	    puts $seqFh $sequence
	    close $seqFh
	} else {
	    set sequence 0
	    if ![file isdirectory $dir/dbase/$fdir] {
		exec mkdir $dir/dbase/$fdir
	    }
	    set seqFh [open $dir/dbase/$fdir/.seq w]
	    puts $seqFh $sequence
	    close $seqFh
	}
	set modSequence ""
	for {set i [expr [string length $sequence]-1]} {$i >= 0} {incr i -1} {
	    set modSequence $modSequence[string index $sequence $i]
	}
	set filename $fdir/$modSequence
	puts $newIndex $filename
	exec mv $dir/dbase/$line(13) $dir/dbase/$filename
    }
    close $newIndex
    close $oldIndex
    set infoFH [open  $dir/index.info w]
    puts $infoFH "3 $entries"
    close $infoFH
    file delete -force -- $dir/index.ver
    file delete -force -- $dir/index.changes
    file delete -force -- $dir/index.read
    exec mv $dir/index.new $dir/index
    file delete -force -- $dir/lock

    # Find unlinked entries
    pack forget $w.scale
    label $w.message -text "Looking for unlinked entries"
    pack $w.message
    update
    set unlinkedList [exec find $dir/dbase -name *@* -print]
    if [llength $unlinkedList] {
	global vFolderDef vFolderDefIdent vFolderStruct

	foreach file $unlinkedList {
	    exec cat $file >>[RatTildeSubst ~/UnlinkedMessages]
	    file delete -force -- $file
	}
	destroy $w
	RatDialog $t(unlinked_messages) \
		"$t(unl_m1) [llength $unlinkedList] $t(unl_m2)" {} 0 \
		$t(continue)
	set vFolderDef($vFolderDefIdent) [list UnlinkedMessages file \
		[RatTildeSubst ~/UnlinkedMessages]]
	set vFolderStruct(0) [linsert $vFolderStruct(0) 0 \
		[list vfolder $vFolderDefIdent UnlinkedMessages]]
	incr vFolderDefIdent
	VFolderWrite
    } else {
	destroy $w
    }
    wm withdraw .
}


# FixOldDbase --
#
# This repairs any inconstencies in the database that are created by
# a fault in the logic in the old version.
#
# Arguments:
# dir -		Directory in which to find dbase

proc FixOldDbase {dir} {
    global option

    # Check for existance
    if { 0 == [file exists $dir/index]} {
	return
    }

    # The database is good so far
    set good 1

    # First check for locks
    if { 1 == [file exists $dir/index.read]} {
	if { 0 < [file size $dir/index.read]} {
	    set result [RatDialog "Dbase in use?" \
					  "I find a lock on the database.\
 Are you running another copy of tkrat somewhere?" {} 1 Yes No ]
	
	    if { $result == 0} {
		# Another copy is runing don't touch the database
		return
	    } else {
		# Possibly corrupt database
		set good 0
		catch "file delete -force -- $dir/index.read"
	    }
	}
    }

    # Now do a quick consistency check of the database
    if { 1 == [file exists $dir/index.lock]} {
	set good 0
	catch "file delete -force -- $dir/index.lock"
    }

    if { 1 == [file exists $dir/index.changes] } {
	set good 0
	catch "file delete -force -- $dir/index.changes"
    }

    if { 0 == [file exists $dir/index.ver] } {
	set good 0
    } else {
	set fh [open $dir/index.ver r]
	gets $fh version
	gets $fh orig_entries
	close $fh
    }

    if { 1 == $good } {
	scan [exec wc -l $dir/index] "%d" lines

	if { [expr ($lines/14)*14] != $lines } {
	    # Not even divisible by 14
	    set good 0
	} else {
	    if { [expr $lines/14] != $orig_entries} {
		# Mismatch with info in index.ver
		set good 0
	    }
	}
    }

    if { 1 == $good } {
	# Dbase seems to be OK
	return
    }

    # Tell the user
    set w .dbaseCorrupt
    toplevel $w -class Postilion
    wm title $w Dbase
    wm iconname $w Dbase

    message $w.msg -text "Database corrupt. Fixing it..." -aspect 800
    pack $w.msg -padx 10 -pady 10

    Place $w fixDbase2
    update

    DoFixDbase $dir

    # Final cleanup
    destroy $w
}


# DoFixDbase --
#
# This routine does the acutual fixing
#
# Arguments:
# dir -		Directory of the dbase

proc DoFixDbase {dir} {

    # Initialize
    set entries 0
    set in [open $dir/index r]
    set out [open $dir/nindex w]

    while { 0 < [gets $in line(0)] && 0 == [eof $in]} {
	# Read 13 lines
	for {set i 1} {$i < 14} {incr i} {
	    gets $in line($i)
	}

	# Check that the last line contains a /< sequence
	while { 0 == [regexp /< $line(13)] } {
	    # Nope, corrupt entry... fix it
	    for {set i 1} {$i < 14} {incr i} {
		if { 1 == [regexp {^ |^	} $line($i)] } {
		    set p [expr $i-1]
		    set line($p) "$line($p)$line($i)"
		    for {set j $i} {$j < 13} {incr j} {
			set line($j) $line([expr $j+1])
		    }
		    gets $in line(13)
		}
	    }

	    if { 1 == [eof $in]} {
		tk_Dialog Error "Can't fix database, giving up" {} 0 Ok
		exit
	    }
	}

	# Write this entry
	for {set i 0} {$i < 14} {incr i} {
	    puts $out $line($i)
	}
	incr entries

	# Consistency check
	if { 1 == [eof $in]} {
	    tk_Dialog Error "Can't fix database, giving up" {} 0 Ok
	    exit
	}
    }

    close $in
    close $out
    exec mv $dir/nindex $dir/index

    set fh [open $dir/index.ver w]
    puts $fh 2
    puts $fh $entries
    close $fh
}

# FixVFolderList --
#
# Upgrade the vfolderlist if needed.
#
# Arguments:

proc FixVFolderList {} {
    global vfolder_list vfolder_def vFolderStructIdent vFolderStruct \
	   vFolderDef vFolderDefIdent vFolderVersion option

    set vFolderStructIdent 0
    set vFolderStruct(0) {}
    if ![info exists vfolder_list] {
	return
    }
    FixVFolderStruct $vfolder_list
    unset vfolder_list

    set vFolderDefIdent 1
    set vFolderDef(0) $option(default_folder)
    set vFolderStruct(0) [linsert $vFolderStruct(0) 0 {vfolder 0 INBOX}]
    foreach vf [array names vfolder_def] {
	if ![info exists vFolderDef($vf)] {
	    continue
	}
	if {$vf > $vFolderDefIdent} {
	    set vFolderDefIdent $vf
	}
	set l $vfolder_def($vf)
	set n $vFolderDef($vf)
	if ![string compare [lindex $l 0] file] {
	    set vFolderDef($vf) [list $n file {} [lindex $l 1]]
	} else {
	    set l2 [lindex $l 2]
	    set vFolderDef($vf) [list $n dbase {} \
		    [lindex $l2 0] [lindex $l2 1] \
		    [string trimleft [lindex $l2 3] +]]
	}
    }
    incr vFolderDefIdent
    set vFolderVersion 4
    VFolderWrite
}

# FixVFolderStruct --
#
# Fixes one menu in the vFolderStruct
#
# Arguments:
# content -	The menu to fix (in the old format)

proc FixVFolderStruct {content} {
    global vFolderStructIdent vFolderStruct vFolderDef

    set ident $vFolderStructIdent
    incr vFolderStructIdent
    foreach elem $content {
	if ![string compare [lindex $elem 1] dir] {
	    lappend vFolderStruct($ident) [list struct \
		    [FixVFolderStruct [lindex $elem 2]] [lindex $elem 0]]
	} else {
	    set vFolderDef([lindex $elem 2]) [lindex $elem 0]
	    lappend vFolderStruct($ident) [list vfolder [lindex $elem 2] \
		    [lindex $elem 0]]
	}
    }
    return $ident
}

# FixOldOptions --
#
# Read old options files and try to adapt to modern options
#
# Arguments:

proc FixOldOptions {} {
    upvar #0 option newOption

    source $newOption(postilion_dir)/postilionrc.gen
    set changed 0

    if [info exists option(show_header)] {
	set newOption(show_header_selection) $option(show_header)
	set changed 1
    }
    if [info exists option(reply_lead)] {
	set newOption(reply_lead) $option(reply_lead)
	set changed 1
    }
    if [info exists option(signature)] {
	set newOption(signature) $option(signature)
	set changed 1
    }
    if [info exists option(xeditor)] {
	set newOption(editor) $option(xeditor)
	set changed 1
    }
    if [info exists option(watcher_geom)] {
	set newOption(watcher_geometry) $option(watcher_geom)
	set changed 1
    }
    if [info exists option(printcmd)] {
	set newOption(print_command) $option(printcmd)
	set changed 1
    }
    if $changed {
	SaveOptions
    }
    file delete -force [RatTildeSubst $newOption(postilion_dir)/postilionrc.gen]
}


# ScanAliases --
#
# See if the user has any old alias files, and if then scan them.
#
# Arguments:

proc ScanAliases {} {
    global option t

    set n 0
    if [file readable ~/.mailrc] {
	incr n [ReadMailAliases ~/.mailrc $option(default_book)]
    }
    if [file readable ~/.elm/aliases.text] {
	incr n [ReadElmAliases ~/.elm/aliases.text $option(default_book)]
    }
    if [file readable ~/.addressbook] {
	incr n [ReadPineAliases ~/.addressbook $option(default_book)]
    }
    if $n {
	Popup "$t(found_aliases) $n $t(num_aliases)."
	foreach book $option(addrbooks) {
	    if {$option(default_book) == [lindex $book 0]} {
		set file [lindex $book 2]
		break
	    }
	}
	RatAlias save $option(default_book) $file
    }

    set option(scan_aliases) 3
    SaveOptions
    AliasesPopulate
}



# AddImapPorts --
#
# Add port spexification to all imap folders (except those that already
# have it.
#
# Arguments:

proc AddImapPorts {} {
    global option vFolderDef

    VFolderRead
    foreach id [array names vFolderDef] {
	if [string compare imap [lindex $vFolderDef($id) 1]] {
	    continue
	}
	set spec [lindex $vFolderDef($id) 2]
	regsub {(\{[^\{\}:]*)\}} $spec "\\1:$option(imap_port)\}" spec
	set vFolderDef($id) [lreplace $vFolderDef($id) 2 2 $spec]
    }
    VFolderWrite
}
