Commits

Dennis T Kaplan  committed c073ca2

v2

  • Participants
  • Parent commits c250bab
  • Tags version2

Comments (0)

Files changed (3)

-[Citadel] Sync
+[Citadel Sync]
 ==============
 
-***************************************************
-## Imports vCards in to the [Citadel] Mail Server
-***************************************************
+************************************
+## Upload to a [Citadel] Mail Server
+************************************
 
-#### Features
+[Citadel Sync] can upload files to a specified room on a remote [Citadel] Mail Server.
 
- - Citadel Sync will work on most operating systems including Linux, Mac, PC, Android ...
- - Converts a multi vCard file in to many for each to hold a single contact
- - Adds UID and REV fields to the vCard if missing
- - Uploads vCards to a given remote [Citadel] Mail Server
- - You can specify, which folder on the server to upload to
- - Can populate the Display name field from first and last name, or populate first and last name from display name
+You may upload any type of text files but Citadel Sync is most useful for
 
+ - Contacts from vCards `.vcf`
+ - Notes from vNotes `.vnt`
+ - Calendar from vCalendars `.vcs` or `.ics`
+ - Task from vCalendars `.vcs` or `.ics`
+ - Text from any text based files `.txt`
 
-#### Features planned
+## HowTo
 
- - Compare revision state and sync in case you modify a vCard on the server. This
- is not supported in version 1, vCards on the server will be over written by the local once.
- - Create an executable for Android etc.
- - Create a single vCard to hold all contacts, so it can be used as an address book in Thunderbird etc.
+	cd
+	mkdir citsync
+	cd citsync
+	wget https://bitbucket.org/gotamer/citadelsync/downloads/citadelsync
+	citadelsync -n contacts
+	nano contacts.cfg.json
 
-#### Features not supported
+The above download link is compatible with any Linux AMD64 based system
+For other systems please see install section below
 
- - Does not support non standard vCard fields like
-   *(If you must have one or two of those let me know)*
- 	- X-EVOLUTION-RADIO
- 	- X-KADDRESSBOOK-X-AssistantsName
+```
+{
+	"Version": 2,
+	"Environment": 1,
+	"LocalDir": "/home/username/pim/contacts",
+	"Room": "Contacts",
+	"Username": "TaMeR",
+	"Password": "God knows what",
+	"Server": "localhost",
+	"Port": ":504",
+	"Floor": "Not implemented",
+	"SSL_KEY": "Not implemented",
+	"SSL_CER": "Not implemented"
+}
+```
 
+#### Version:
+Do not change unless prompted after an upgrade.
 
-#### Hints:
- - Backup your vCards before you start using citadelsync, both local and server
- - Backup your config file before you upgrade to a new version of citadelsync
+#### Environment:
 
-#### Importent Note:
-In order to keep in sync version 1 of citadelsync deletes all contacts, in the given room on your citadel server, then uploads the vCards from your local folder to the room!
+	1. Production
+	2. Info mode, prints a lot of info in to the log file
+	3. Debug mode, will print to screen, and exit if it finds something not quite right
 
+#### LocalDir:
+Point to the local folder containing your vCards, vNotes etc. files
 
-### Command Line Flags:
+#### Room:
+The Citadel Room to upload to
 
-A config file is required, set it with the -c flag.
+#### Username and Password
+Your Citadel username or password. Keep empty "" to specify on the command line
 
-If the specified config file does not exist, one will be created with default values.
+#### Server:
+Your Citadel hostname.
 
- > -D will delete all items on the remote server, in the given room WITHOUT WARNING
+#### Port:
+Your Citadel port. Standard is 504 if you haven't changed it on the server.
 
-Username and Password for the [Citadel] Mail Server may be
-defined in the config file, or optionally on the command line
+_____________________
+### When you are done editing run again
 
-The -r Flag checks if the room exists on the mail server. You
-can use this to verify that you have spelled the room name correctly
+	citadelsync -n contacts
 
+This will
 
-	-v=false: version
-	-h=false: Prints out this help text
-	-c="citadelVcard.json": Config file (*.json)
-	-u="": Username
-	-p="": Password
-	-r=false: Check if the citadel room even exists
-	-D=false: Delete all items in the room!
-	-i="": Import file (*.vcf)
+ 1. Check your server connection
+ 2. Your login information
+ 3.	The availability of the room
+ 4. The compatibillity of the room with your file type
+ 5. Your LocalDir
 
-#### The Environment flags in the config file are:
+If everything checks out it will upload all files from your LocalDir to the specified Room on the remote Citadel Server.
 
-	0 = Production
-	1 = Prints a lot of info
-	2 = Debug mode, same as 1 but will exit on error
+_____________________
+
+### Notes
+
+ * Files must be individual files each holding a single entry residing in one folder, anywhere on your computer or device.
+
+ * Citadel Sync remembers the state of your files and will only upload modified files
+
+ * Citadel Sync will not delete or modify any files it didn't upload, unless you use the -D flag.
 
 ## Install
 
 ### Executable
 
- > There is an executable version for Linux AMD64 at:
-https://bitbucket.org/gotamer/citadel/downloads/citadelsync
+There is an executable version for Linux AMD64 at:
+
+https://bitbucket.org/gotamer/citadelsync/downloads/citadelsync
 
 ### From Source
 
  > Install go then run
 
-	go get bitbucket.org/gotamer/citadel
+	go get bitbucket.org/gotamer/citadelsync
+
+
+
 
-________________________________________________________
+_____________________
 
 The MIT License (MIT)
-========================================================
+=====================
 
 Copyright © 2013 Dennis T Kaplan <http://www.robotamer.com>
 
 
 
 [Citadel]:(http://www.citadel.org "Citadel")
-[GoDoc]:(https://godoc.org/bitbucket.org/gotamer/citadel)
+[Citadel Sync]:(http://bitbucket.org/gotamer/citadelsync)
+[GoDoc]:(https://godoc.org/bitbucket.org/gotamer/citadelsync)
+// Import vCards to the Citadel Mail Server
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+	"time"
+
+	"bitbucket.org/gotamer/cfg"
+	"bitbucket.org/gotamer/citadel"
+	"bitbucket.org/gotamer/errors"
+	"bitbucket.org/gotamer/tools"
+)
+
+const (
+	VERSION      = 2
+	VCARD_TIME   = "20060102T150405Z0700"
+	DEFAULT_PORT = ":504"
+)
+
+var (
+	name     = flag.String("n", "", "Name of the configuration contacts, notes, calender etc.")
+	helpflag = flag.Bool("h", false, "Prints out this help text")
+	username = flag.String("u", "", "Citadel Username")
+	password = flag.String("p", "", "Citadel Password")
+	purge    = flag.Bool("D", false, "Delete all items in the room!")
+	version  = flag.Bool("v", false, "version")
+)
+
+var helpTop = `
+  ****************************************
+               Citadel Sync
+  ****************************************
+
+  Upload files to a Citadel Mail Server
+
+ - Contacts from vCards ".vcf"
+ - Notes from vNotes ".vnt"
+ - Calendar from vCalendars ".vcs" or ".ics"
+ - Task from vCalendars ".vcs" or ".ics"
+ - Text from any text based files ".txt"
+
+`
+
+var helpButtom = `
+
+  -n Based on the name flag a configuration template file will be created
+
+  -u and -p / Username and Password for the Citadel Mail Server may be
+  defined in the config file, or optionaly on the command line
+
+  -D will delete all items in the given room WITHOUT WARNING
+
+`
+
+var (
+	PS       string = string(os.PathSeparator)
+	Cfg      *config
+	FILE_DB  string
+	FILE_CFG string
+	FILE_LOG string
+	DB       map[string]*database
+)
+
+type config struct {
+	Version     int8
+	Environment e.Env
+	LocalDir    string
+	Room        string
+	Username    string
+	Password    string
+	Server      string
+	Port        string
+	Floor       string
+	SSL_KEY     string
+	SSL_CER     string
+	citRoomType string
+}
+
+func init() {
+	Cfg = new(config)
+	Cfg.Version = VERSION
+	Cfg.Environment = e.ENV_PROD
+	Cfg.Username = "TaMeR"
+	Cfg.Password = "God knows what"
+	Cfg.Server = "localhost"
+	Cfg.Port = DEFAULT_PORT
+	Cfg.Room = "Contacts"
+	Cfg.LocalDir = os.TempDir() + PS + "vcard"
+	Cfg.Floor = "Not implemented"
+	Cfg.SSL_CER = "Not implemented"
+	Cfg.SSL_KEY = "Not implemented"
+
+	DB = make(map[string]*database)
+}
+
+func main() {
+	flag.Parse()
+	if *version {
+		fmt.Printf("\n\tCitadel Import Version %v\n\n", VERSION)
+		os.Exit(0)
+	}
+	if *helpflag || *name == "" {
+		fmt.Println(helpTop)
+		flag.PrintDefaults()
+		fmt.Println(helpButtom)
+		os.Exit(0)
+	}
+	FILE_DB = fmt.Sprintf("%s.db.json", *name)
+	FILE_CFG = fmt.Sprintf("%s.cfg.json", *name)
+	FILE_LOG = fmt.Sprintf("%s.log", *name)
+
+	if err := cfg.Load(FILE_CFG, Cfg); err != nil {
+		Cfg.LocalDir = os.Getenv("HOME") + PS + "PIM" + PS + *name
+		if err = cfg.Save(FILE_CFG, Cfg); err != nil {
+			fmt.Println("cfg.Save error: ", err.Error())
+			os.Exit(1)
+		} else {
+			fmt.Printf("\nPlease edit your config file at:\n\n\t%s\n", FILE_CFG)
+			os.Exit(0)
+		}
+	}
+
+	if Cfg.Version != VERSION {
+		fmt.Println("Please check and upgrade your config file version to the new version: ", VERSION)
+		os.Exit(0)
+	}
+
+	LogFile()
+
+	if len(*username) != 0 {
+		Cfg.Username = *username
+	}
+
+	if len(*password) != 0 {
+		Cfg.Password = *password
+	}
+
+	if *purge {
+		CitadelDeleteAll()
+		os.Exit(0)
+	}
+
+	if !checkRoom() {
+		os.Exit(0)
+	}
+
+	if Cfg.Server != "" {
+		FilesInfo()
+		//fmt.Println(DB)
+		CitadelSend()
+	}
+}
+
+type database struct {
+	CitEUID      string
+	FileName     string
+	FileMimeType string
+	FileModTime  time.Time
+	fileModified bool
+}
+
+func (db *database) Set() {
+	DB[db.FileName] = db
+}
+
+func (db *database) Del() {
+	delete(DB, db.FileName)
+}
+
+func (db *database) Modified(fi os.FileInfo) bool {
+	if db.FileName != "" {
+		if db.FileModTime != fi.ModTime() {
+			db.fileModified = true
+		}
+	} else {
+		db.FileModTime = fi.ModTime()
+		db.FileName = fi.Name()
+		db.fileModified = true
+		DB[db.FileName] = db
+	}
+	return db.fileModified
+}
+
+// TODO use linux "file -bip"
+func (db *database) MimeType() (ok bool) {
+	if db.FileName != "" {
+		suffix := Suffix(db.FileName)
+		switch suffix {
+		case "vcf":
+			db.FileMimeType = "Content-type: text/x-vcard"
+			ok = true
+			if Cfg.citRoomType != citadel.VIEW_ADDRESSBOOK {
+				e.Info("WARNING not a vCard Room. Code is: %v", Cfg.citRoomType)
+			}
+		case "vnt":
+			db.FileMimeType = "Content-type: text/vnote"
+			ok = true
+			if Cfg.citRoomType != citadel.VIEW_NOTES {
+				e.Info("WARNING not a vNote Room. Code is: %v", Cfg.citRoomType)
+			}
+		case "vcs", "ics":
+			db.FileMimeType = "Content-type: text/calendar"
+			ok = true
+			if Cfg.citRoomType != citadel.VIEW_CALENDAR {
+				e.Info("WARNING not a Calender Room. Code is: %v", Cfg.citRoomType)
+			}
+			if Cfg.citRoomType != citadel.VIEW_TASKS {
+				e.Info("WARNING not a Task Room. Code is: %v", Cfg.citRoomType)
+			}
+		case "txt":
+			db.FileMimeType = "Content-type: text/text"
+			ok = true
+			if Cfg.citRoomType != citadel.VIEW_MAILBOX || Cfg.citRoomType != citadel.VIEW_BBS {
+				e.Info("WARNING not a text Room. Code is: %v", Cfg.citRoomType)
+			}
+		default:
+			db.FileMimeType = "Content-type: text/text"
+			e.Info("WARNING Can't determen file type, using text!")
+		}
+	}
+	return
+}
+
+func (db *database) CitadelEUID() {
+	if db.FileName != "" {
+		if db.CitEUID == "" {
+			db.CitEUID = tools.Uid16()
+			db.fileModified = true
+		}
+	}
+}
+
+func FilesInfo() {
+	fis := readDir(Cfg.LocalDir)
+	no := len(fis)
+	for i := 0; i < no; i++ {
+		if fis[i].IsDir() {
+			continue
+		}
+		dbitem, ok := DB[fis[i].Name()]
+		if !ok {
+			dbitem = new(database)
+		}
+
+		dbitem.Modified(fis[i])
+		dbitem.MimeType()
+		dbitem.CitadelEUID()
+	}
+}
+
+func readDir(path string) (fis []os.FileInfo) {
+	fi, err := os.Stat(path)
+	e.Check(err)
+	if fi.IsDir() {
+		f, err := os.Open(path)
+		defer f.Close()
+		e.Check(err)
+		fis, err = f.Readdir(0) // 0 = All
+		e.Check(err)
+	}
+	return
+}
+
+func CitadelSend() {
+	c := citadel.New(Cfg.Server + Cfg.Port)
+	defer c.Close()
+	c.Login(Cfg.Username, Cfg.Password)
+	c.Goto(Cfg.Room)
+	c.Info()
+
+	for _, dbitem := range DB {
+		if dbitem.fileModified == false {
+			e.Info("Not modified, not sending!")
+			continue
+		}
+
+		bytes, err := ioutil.ReadFile(Cfg.LocalDir + PS + dbitem.FileName)
+		ok := e.Check(err)
+		if ok {
+			cmd := fmt.Sprintf("ENT0 1|||4||||||%s", dbitem.CitEUID)
+			c.Request(cmd)
+			if c.Code == citadel.CODE_SEND_LISTING {
+				c.Error = c.Conn.PrintfLine(dbitem.FileMimeType)
+				c.Check()
+				err = c.Conn.PrintfLine("%s", "\n")
+				e.Check(err)
+				err = c.Conn.PrintfLine("%s", bytes)
+				e.Check(err)
+				err = c.Conn.PrintfLine("%s", citadel.DE)
+				e.Check(err)
+				e.Info("%s", bytes)
+			}
+		}
+	}
+}
+
+func CitadelDeleteAll() {
+	c := citadel.New(Cfg.Server + Cfg.Port)
+	defer c.Close()
+	c.Login(Cfg.Username, Cfg.Password)
+	c.Goto(Cfg.Room)
+	c.Info()
+	list, ok := c.MsgListAll()
+	if ok {
+		c.MsgsDel(list)
+	}
+}
+
+func checkRoom() (ok bool) {
+	c := citadel.New(Cfg.Server + Cfg.Port)
+	defer c.Close()
+	c.Login(Cfg.Username, Cfg.Password)
+	if ok = c.Goto(Cfg.Room); !ok {
+		e.Info("Room not availabe")
+		c.Info()
+	} else {
+		Cfg.citRoomType = c.Resp[12]
+	}
+	return
+}
+
+func DBLoad() {
+	if err := cfg.Load(FILE_DB, &DB); err != nil {
+		e.Info("Will create new database at: %v", FILE_DB)
+	}
+}
+
+func DBSave() {
+	if err := cfg.Save(FILE_DB, DB); err != nil {
+		e.Info("Could not create database at: ", FILE_DB)
+		fmt.Println("Could not create database at: %v", FILE_DB)
+	}
+}
+
+func LogFile() {
+	e.ENVIRONMENT = Cfg.Environment
+	if e.ENVIRONMENT != e.ENV_FAIL {
+		f, err := os.OpenFile(FILE_LOG, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
+		e.Check(err)
+		e.Logger(f)
+	}
+}
+
+func Suffix(filename string) (suffix string) {
+	no := strings.LastIndex(filename, ".")
+	if no > 0 {
+		suffix = filename[no+1:]
+	}
+	return
+}

File vcard.go

-// Import vCards to the Citadel Mail Server
-package main
-
-import (
-	"bufio"
-	"bytes"
-	"flag"
-	"fmt"
-	"io"
-	"os"
-	"strings"
-	"time"
-
-	"bitbucket.org/gotamer/cfg"
-	"bitbucket.org/gotamer/citadel"
-	"bitbucket.org/gotamer/errors"
-	"bitbucket.org/gotamer/tools"
-	"bitbucket.org/llg/vcard"
-)
-
-const (
-	VERSION      = 1
-	VCARD_TIME   = "20060102T150405Z0700"
-	DEFAULT_PORT = ":504"
-	VCARD_DB     = "citadelVcardDB.json"
-)
-
-var (
-	cfg_file    = flag.String("c", "citadelVcard.json", "Config file (*.json)")
-	helpflag    = flag.Bool("h", false, "Prints out this help text")
-	checkroom   = flag.Bool("r", false, "Check if room exists")
-	username    = flag.String("u", "", "Username")
-	password    = flag.String("p", "", "Password")
-	purge       = flag.Bool("D", false, "Delete all items in the room!")
-	version     = flag.Bool("v", false, "version")
-	import_file = flag.String("i", "", "Import file (*.vcf)")
-)
-
-var help = `
-  ***************************************************
-       Import vCards to Citadel
-  ***************************************************
-  A config file is requiered, set it with the -c flag.
-
-  If the specified config file does not exist, one
-  will be created with default values.
-
-  -D will delete all items in the given room WITHOUT WARNING
-
-  Optionaly you may specify an import path, to import vcards.
-  The import path may be a file, or a folder.
-
-  Username and Password for the Citadel Mail Server may be
-  defined in the config file, or optionaly on the command line
-
-  Environment:
-	0 = Production
-	1 = Prints alot of info
-	2 = Debug mode, same as 1 but will exit on error
-
-  The -r Flag checks if the room exists one mail server. You
-  can use this to verify that you have spelled the room name correctly
-
-  Hint: Don't use the default config file name if you
-  are planing to have more then one configuration.
-`
-
-var (
-	PS          string = string(os.PathSeparator)
-	addressBook vcard.AddressBook
-	Cfg         *config
-)
-
-type config struct {
-	Version     int8
-	Environment e.Env
-	PathVcard   string
-	Room        string
-	Username    string
-	Password    string
-	Server      string
-	Port        string
-	LogFile     string
-	Floor       string
-	SSL_KEY     string
-	SSL_CER     string
-}
-
-var DB map[string]*vCard
-
-type vCard struct {
-	UID      string
-	REV      string
-	Modified bool
-}
-
-func (v *vCard) Set() {
-	DB[v.UID] = v
-}
-
-func (v *vCard) Del() {
-	delete(DB, v.UID)
-}
-
-func DBLoad() {
-	file := Cfg.PathVcard + PS + VCARD_DB
-	if err := cfg.Load(file, DB); err != nil {
-		e.Info("Creating database at: %v", file)
-	}
-}
-
-func DBSave() {
-	file := Cfg.PathVcard + PS + VCARD_DB
-	if err := cfg.Save(file, DB); err != nil {
-		e.Info("Could not create database at: ", file)
-		fmt.Println("Could not create database at: %v", file)
-	} else {
-		fmt.Println("Created database at: ", file)
-	}
-}
-
-func init() {
-	Cfg = new(config)
-	Cfg.Version = VERSION
-	Cfg.Environment = e.ENV_PROD
-	Cfg.Username = "TaMeR"
-	Cfg.Password = "God knows what"
-	Cfg.Server = "localhost"
-	Cfg.Port = DEFAULT_PORT
-	Cfg.Room = "Contacts"
-	Cfg.PathVcard = os.TempDir() + PS + "vcard"
-	Cfg.Floor = "Not implemented"
-	Cfg.SSL_CER = "Not implemented"
-	Cfg.SSL_KEY = "Not implemented"
-	Cfg.LogFile = "citadelVcard.log"
-	DB = make(map[string]*vCard)
-	DBLoad()
-}
-
-func LogFile() {
-	e.ENVIRONMENT = Cfg.Environment
-	if Cfg.LogFile != "" {
-		f, err := os.OpenFile(Cfg.LogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664)
-		e.Check(err)
-		e.Logger(f)
-	}
-}
-
-func main() {
-	flag.Parse()
-	if *version {
-		fmt.Printf("\n\tCitadel Import Version %v\n\n", VERSION)
-		os.Exit(0)
-	}
-	if *helpflag {
-		fmt.Println(help)
-		flag.PrintDefaults()
-		fmt.Println("\n")
-		os.Exit(0)
-	}
-	if err := cfg.Load(*cfg_file, Cfg); err != nil {
-		if err = cfg.Save(*cfg_file, Cfg); err != nil {
-			fmt.Println("cfg.Save error: ", err.Error())
-			os.Exit(1)
-		} else {
-			fmt.Printf("\nPlease edit your config file at:\n\n\t%s\n", *cfg_file)
-			os.Exit(0)
-		}
-	}
-
-	if Cfg.Version != VERSION {
-		fmt.Println("Please check and upgrade your config file version to the new version: ", VERSION)
-		os.Exit(0)
-	}
-
-	LogFile()
-
-	if len(*username) != 0 {
-		Cfg.Username = *username
-	}
-	if len(*password) != 0 {
-		Cfg.Password = *password
-	}
-
-	if *purge {
-		CitadelDeleteAll()
-		os.Exit(0)
-	}
-
-	if *checkroom {
-		checkRoom()
-		os.Exit(0)
-	}
-
-	VcardsLoad()
-	if *import_file != "" {
-		VcardsImport(*import_file)
-	}
-	VcardsCheck()
-	VcardsDelete()
-	VcardsSave()
-	if Cfg.Server != "" {
-		CitadelSend()
-	}
-}
-
-// TODO: Get from server and save the message id in the DB, then delete from server
-// the once we added to avoid duplicates.
-func CitadelSend() {
-
-	c := citadel.New(Cfg.Server + Cfg.Port)
-	defer c.Close()
-	c.Login(Cfg.Username, Cfg.Password)
-	c.Goto(Cfg.Room)
-	c.Info()
-	var err error
-
-	for _, db := range DB {
-		if db.Modified == false {
-			continue
-		}
-
-		card := VcardLoad(db.UID)
-		if card == nil {
-			continue
-		}
-		contact := VcardString(card)
-
-		c.Request("ENT0 1|||4")
-		if c.Code == citadel.CODE_SEND_LISTING {
-			c.Error = c.Conn.PrintfLine("Content-type: text/x-vcard; charset=UTF-8")
-			c.Check()
-			err = c.Conn.PrintfLine("%s", "\n")
-			e.Check(err)
-			err = c.Conn.PrintfLine("%s", contact)
-			e.Check(err)
-			err = c.Conn.PrintfLine("%s", citadel.DE)
-			e.Check(err)
-			e.Info("%s", contact)
-		}
-	}
-}
-
-// one at a time when sending to citadel
-func VcardLoad(filename string) (contact *vcard.AddressBook) {
-	file := fmt.Sprintf("%s%s%s.vcf", Cfg.PathVcard, PS, filename)
-	f, err := os.Open(file)
-	if err != nil {
-		e.LogError.Printf("Can't read file %s\n", file)
-		return
-	}
-	reader := vcard.NewDirectoryInfoReader(f)
-	contact = new(vcard.AddressBook)
-	contact.ReadFrom(reader)
-	f.Close()
-	return contact
-}
-
-func CitadelSendBakup() {
-
-	c := citadel.New(Cfg.Server + Cfg.Port)
-	defer c.Close()
-	c.Login(Cfg.Username, Cfg.Password)
-	c.Goto(Cfg.Room)
-	c.Info()
-	var err error
-
-	fis := readDir(Cfg.PathVcard)
-	var no = len(fis)
-	for i := 0; i < no; i++ {
-		fi := fis[i]
-		card := VcardLoadFI(fi)
-		if card == nil {
-			continue
-		}
-		contact := VcardString(card)
-
-		c.Request("ENT0 1|||4")
-		if c.Code == citadel.CODE_SEND_LISTING {
-			c.Error = c.Conn.PrintfLine("Content-type: text/x-vcard; charset=UTF-8")
-			c.Check()
-			err = c.Conn.PrintfLine("%s", "\n")
-			e.Check(err)
-			err = c.Conn.PrintfLine("%s", contact)
-			e.Check(err)
-			err = c.Conn.PrintfLine("%s", citadel.DE)
-			e.Check(err)
-			e.Info("%s", contact)
-		}
-	}
-}
-
-func CitadelDeleteAll() {
-	c := citadel.New(Cfg.Server + Cfg.Port)
-	defer c.Close()
-	c.Login(Cfg.Username, Cfg.Password)
-	c.Goto(Cfg.Room)
-	c.Info()
-	list, ok := c.MsgListAll()
-	if ok {
-		ok = c.MsgsDel(list)
-		if ok {
-			list, _ := c.MsgListAll()
-			fmt.Printf("list: %s\n\n", list)
-		}
-	}
-}
-
-func VcardString(address *vcard.AddressBook) string {
-	var b = new(bytes.Buffer)
-	writer := vcard.NewDirectoryInfoWriter(b)
-	address.WriteTo(writer)
-	return b.String()
-}
-
-// one at a time when sending to citadel
-func VcardLoadFI(fi os.FileInfo) (contact *vcard.AddressBook) {
-	if fi.IsDir() {
-		e.LogError.Printf("Path must be a vcatd file, this is a folder: %s\n", fi.Name())
-		return
-	}
-	if fi.Size() < 200 {
-		e.LogError.Printf("File is too small for a vcard: %s\n", fi.Name())
-		return
-	}
-	if !strings.HasSuffix(fi.Name(), ".vcf") {
-		e.LogError.Printf("File must be a vcard ending with .vcf Got: %s\n", fi.Name())
-		return
-	}
-	file := fmt.Sprintf("%s%s%s", Cfg.PathVcard, PS, fi.Name())
-	f, err := os.Open(file)
-	if err != nil {
-		e.LogError.Printf("Can't read file %s\n", file)
-		return
-	}
-	reader := vcard.NewDirectoryInfoReader(f)
-	contact = new(vcard.AddressBook)
-	contact.ReadFrom(reader)
-	f.Close()
-	return contact
-}
-
-func VcardsLoad() {
-	fis := readDir(Cfg.PathVcard)
-	no := len(fis)
-	for i := 0; i < no; i++ {
-		if fis[i].IsDir() {
-			continue
-		}
-		if fis[i].Size() < 100 {
-			continue
-		}
-		file := fmt.Sprintf("%s%s%s", Cfg.PathVcard, PS, fis[i].Name())
-		if !strings.HasSuffix(file, ".vcf") {
-			continue
-		}
-		f, err := os.Open(file)
-		if err != nil {
-			e.LogError.Printf("Can't read file %s\n", file)
-			continue
-		}
-		reader := vcard.NewDirectoryInfoReader(f)
-		addressBook.ReadFrom(reader)
-
-		// Checking if the vcard is in the database, if not we are adding it
-		// to the delete list. It will be saved with the standard UID name.
-		noc := len(addressBook.Contacts)
-		if noc > 0 {
-			uid := addressBook.Contacts[noc-1].UID
-			_, ok := DB[uid]
-			if !ok {
-				noc := len(deleteList)
-				var found bool
-				if noc > 0 {
-					it := NewIteratorIntNo(noc)
-					for it.Next() {
-						if deleteList[it.Value()] == file {
-							found = true
-							break
-						}
-					}
-				}
-				if found == false {
-					deleteList = append(deleteList, file)
-				}
-			}
-		}
-		f.Close()
-	}
-}
-
-type iteratorInt struct {
-	current int
-	data    []int
-}
-
-func (it *iteratorInt) Value() int {
-	return it.data[it.current]
-}
-func (it *iteratorInt) Next() bool {
-	it.current++
-	if it.current >= len(it.data) {
-		return false
-	}
-	return true
-}
-func NewIteratorIntNo(no int) *iteratorInt {
-	var data []int
-	for i := 0; i < no; i++ {
-		data = append(data, i)
-	}
-	return &iteratorInt{data: data, current: -1}
-}
-
-var deleteList []string // List of files to delete after saving vcards
-
-func readDir(path string) (fis []os.FileInfo) {
-	fi, err := os.Stat(path)
-	e.Check(err)
-	if fi.IsDir() {
-		f, err := os.Open(path)
-		defer f.Close()
-		e.Check(err)
-		fis, err = f.Readdir(0) // 0 = All
-		e.Check(err)
-	}
-	return
-}
-
-func VcardsImport(path string) {
-	fi, err := os.Stat(path)
-	e.Check(err)
-	if fi.IsDir() {
-		f, err := os.Open(path)
-		if err != nil {
-			e.LogError.Printf("Can't read file %s\n", path)
-			return
-		}
-		fil, err := f.Readdir(0) // 0 = All
-		f.Close()
-		no := len(fil)
-		for i := 0; i < no; i++ {
-			if fil[i].IsDir() {
-				continue
-			}
-			if fil[i].Size() < 200 {
-				continue
-			}
-			file := fmt.Sprintf("%s%s%s", path, PS, fil[i].Name())
-			if !strings.HasSuffix(file, ".vcf") {
-				continue
-			}
-			f, err := os.Open(file)
-			if err != nil {
-				e.LogError.Printf("Can't read file %s\n", file)
-				continue
-			}
-			reader := vcard.NewDirectoryInfoReader(f)
-			addressBook.ReadFrom(reader)
-			f.Close()
-		}
-	} else {
-		if fi.Size() < 200 {
-			e.Info("File too small %s", fi.Size())
-			return
-		}
-		if !strings.HasSuffix(path, ".vcf") {
-			e.Info("Wrong file type")
-			return
-		}
-		f, err := os.Open(path)
-		e.Check(err)
-		defer f.Close()
-		reader := vcard.NewDirectoryInfoReader(f)
-		addressBook.ReadFrom(reader)
-	}
-
-}
-
-func VcardsSave() {
-	for _, c := range addressBook.Contacts {
-		var address = new(vcard.AddressBook)
-		address.Contacts = append(address.Contacts, c)
-		uid := address.Contacts[0].UID
-
-		// Check if modified flag is set, we will only save if it is.
-		if DB[uid].Modified == false {
-			continue
-		}
-
-		path := fmt.Sprintf("%s%s%s.vcf", Cfg.PathVcard, PS, uid)
-		file, err := os.Create(path)
-		e.Check(err)
-		defer file.Close()
-		bufoutput := bufio.NewWriter(file)
-		var output io.Writer
-		output = bufoutput
-		defer bufoutput.Flush()
-		writer := vcard.NewDirectoryInfoWriter(output)
-		address.WriteTo(writer)
-	}
-}
-
-func VcardsDelete() {
-	var ok bool
-	var err error
-	for i, d := range deleteList {
-		if d != "" {
-			err = os.Remove(d)
-			if ok = e.Check(err); ok {
-				deleteList[i] = ""
-			}
-		}
-	}
-}
-
-func VcardsCheck() {
-	for i := 0; i < len(addressBook.Contacts); i++ {
-		contact := &(addressBook.Contacts[i])
-		var modified bool
-
-		origianal := contact
-
-		if len(contact.REV) != 16 {
-			e.Info("Modified: REV should be 16 but is %s", contact.REV)
-			modified = true
-		}
-
-		if len(contact.UID) < 2 {
-			e.Info("Modified: UID!")
-			contact.UID = tools.Uid16()
-			modified = true
-		}
-
-		_, ok := DB[contact.UID]
-		if !ok {
-			modified = true // This will make sure that the new contact will be added to the DB
-		}
-		if modified {
-			e.Info("Org: %s", origianal)
-			contact.REV = time.Now().UTC().Format(VCARD_TIME)
-
-			v := new(vCard)
-			v.UID = contact.UID
-			v.REV = contact.REV
-			v.Modified = modified
-			v.Set()
-
-			e.Info("New: %s", contact)
-		}
-	}
-}
-
-func fixFamilyName(contact *vcard.VCard) {
-	if len(contact.FamilyNames) == 0 {
-		if len(contact.FormattedName) > 1 {
-			fn := strings.Split(contact.FormattedName, " ")
-			no := len(fn)
-			contact.FamilyNames = []string{fn[no-1]}
-		}
-	}
-}
-
-func fixGivenName(contact *vcard.VCard) {
-	if len(contact.GivenNames) == 0 {
-		if len(contact.FormattedName) > 1 {
-			fn := strings.Split(contact.FormattedName, " ")
-			contact.GivenNames = []string{fn[0]}
-		}
-	}
-}
-
-func checkRoom() {
-	c := citadel.New(Cfg.Server + Cfg.Port)
-	defer c.Close()
-	c.Login(Cfg.Username, Cfg.Password)
-	c.Goto(Cfg.Room)
-	list, _ := c.MsgListAll()
-	fmt.Printf("\nList id of contacts in room:\n%s\n\n", list)
-}