Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
3,625 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Compiled source # | ||
################### | ||
*.com | ||
*.class | ||
*.dll | ||
*.exe | ||
*.o | ||
*.so | ||
|
||
# Packages # | ||
############ | ||
# it's better to unpack these files and commit the raw source | ||
# git has its own built in compression methods | ||
*.7z | ||
*.dmg | ||
*.gz | ||
*.iso | ||
*.jar | ||
*.rar | ||
*.tar | ||
*.zip | ||
|
||
# Logs and databases # | ||
###################### | ||
*.log | ||
*.sql | ||
*.sqlite | ||
|
||
# OS generated files # | ||
###################### | ||
.DS_Store | ||
.DS_Store? | ||
._* | ||
.Spotlight-V100 | ||
.Trashes | ||
ehthumbs.db | ||
Thumbs.db |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,77 @@ | ||
# KoboMail | ||
Experimental email attachment downloader for Kobo devices (gmail only ATM) | ||
|
||
## What is KoboMail? | ||
It is a software that will read emails sent to user+kobo@gmail.com and download the attached files to the Kobo device. | ||
It doesn't restrict the filetype so try to keep the emails you send as clean as possible ;) | ||
The software takes advantage of a particularity in Gmail which is you can add flags to you email like the +kobo in the example above and still receive those emails on the user@gmail.com account. This makes it much easier for searching and listing emails specifically targetting your Kobo device. | ||
Once the email is treated by program it will automatically flag it as seen so it's not processed twice (there's a flag in the config file that allows overriding for such time you want to redownload all files you've sent). | ||
|
||
### Warning | ||
|
||
This software is experimental, if you don't want to risk corrupting your files, database, etc of your Kobo device don't use this. | ||
Use this software AT YOUR OWN RISK. | ||
|
||
## Changelog | ||
**0.1.0** | ||
* Initial release | ||
|
||
## Installing | ||
|
||
Quick start for Kobo devices: | ||
|
||
1. download the latest [KoboRoot.tgz](https://github.com/clisboa/KoboMail/releases/download/v0.1/KoboRoot.tgz) | ||
1. connect your Kobo device to your computer with a USB cable | ||
3. place the KoboRoot.tgz file in the .kobo directory of your Kobo device | ||
4. disconnect the reader | ||
|
||
When you disconnect the Kobo device, it will perform the instalation of the KoboRoot.tgz files onto the device. | ||
Once the installation is finished you can verify that KoboRoot.tgz is now gone. | ||
No you should head to the .add/kobomail folder and edit the kobomail_cfg.toml file | ||
|
||
|
||
``` | ||
# currently only gmail is supported | ||
# you need to activate IMAP for your gmail account | ||
imap_host = "imap.gmail.com" | ||
imap_port = "993" | ||
# gmail account | ||
imap_user = "user@gmail.com" | ||
# gmail app password. you will need to generate a password specifically for KoboMail | ||
# this can be done here: https://support.google.com/mail/answer/185833?hl=en-GB | ||
imap_pwd = "password" | ||
# with gmail you can send an email to user+kobo@gmail.com and the email will land on user@gmail.com account | ||
# you can customize the flag used to detect the emails you want to process specifically for the Kobo device | ||
email_flag = "kobo" | ||
# flag to process all emails sent fo user+kobo@gmail.com or only the unread emails | ||
email_unseen = "true" | ||
# If you want to uninstall KoboMail just place an empty file called UNINSTALL next to this configuration file | ||
# and next time KoboMail runs it will delete itself | ||
``` | ||
|
||
If the configuration is not correct KoboMail might not be able to work correctly. | ||
You will need to activate IMAP on your gmail account and generate an app password as described in the config file. | ||
Once the configuration is correct everytime your device connects to a Wifi access point the KoboMail program will run and process any emails sent to user+kobo@gmail.com that are not open yet. | ||
If any messages were processed after a few seconds Kobo will display the dialog to connect to a PC, you don't need to actually physically connect a USB cable you just need to click on the Connect button. This is part of a workarround to trigger Kobo to recognize the new ebooks it just received via email. | ||
After clicking on the connect button you will see the common full screen dialog as if Kobo was connected to a PC and shortly after it will show the import content progress bar. | ||
|
||
You can attach multiple files to a single email, every attachment will be processed. All attachments will be dumped into the folder KoboMailLibrary. | ||
|
||
There's a log.txt file in the .add/kobomail folder that will allow to diagnose problems. | ||
|
||
## Uninstalling | ||
|
||
Just place a file called UNINSTALL in the .add/kobomail folder and everything will be wiped clean except the KoboMailLibrary~. | ||
|
||
## Further information. | ||
This project includes bits and pieces of many different projects and ideas discussed in the mobileread.com forums, namely: | ||
- https://github.com/shermp/kobo-rclone | ||
- https://github.com/fsantini/KoboCloud | ||
- https://gitlab.com/anarcat/wallabako | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
KERNEL=="eth*", ACTION=="add", RUN+="/usr/local/kobomail/kobomail_launcher.sh" | ||
KERNEL=="wlan*", ACTION=="add", RUN+="/usr/local/kobomail/kobomail_launcher.sh" | ||
KERNEL=="lo", RUN+="/usr/local/kobomail/kobomail_config_setup.sh" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module github.com/clisboa/kobomail | ||
|
||
go 1.17 | ||
|
||
require ( | ||
github.com/BurntSushi/toml v0.4.1 // indirect | ||
github.com/emersion/go-imap v1.2.0 // indirect | ||
github.com/emersion/go-message v0.15.0 // indirect | ||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect | ||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect | ||
github.com/gwatts/rootcerts v0.0.0-20210901182307-6417658c2540 // indirect | ||
github.com/leprosus/golang-log v1.0.11 // indirect | ||
golang.org/x/text v0.3.7 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= | ||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= | ||
github.com/emersion/go-imap v1.2.0 h1:lyUQ3+EVM21/qbWE/4Ya5UG9r5+usDxlg4yfp3TgHFA= | ||
github.com/emersion/go-imap v1.2.0/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY= | ||
github.com/emersion/go-message v0.15.0 h1:urgKGqt2JAc9NFJcgncQcohHdiYb803YTH9OQwHBHIY= | ||
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4= | ||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ= | ||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= | ||
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac h1:tn/OQ2PmwQ0XFVgAHfjlLyqMewry25Rz7jWnVoh4Ggs= | ||
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= | ||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY= | ||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= | ||
github.com/gwatts/rootcerts v0.0.0-20210901182307-6417658c2540 h1:33heCgbF4SKPnsQoFF5xn9aEu7EY6gWDX/8pxyhYHfo= | ||
github.com/gwatts/rootcerts v0.0.0-20210901182307-6417658c2540/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g= | ||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||
github.com/leprosus/golang-log v1.0.11 h1:sy5c0KMwESzuosD5DOyQ8b73F7M1zeFReL0O2ESJsRk= | ||
github.com/leprosus/golang-log v1.0.11/go.mod h1:ekiK3/I7IQHUMor8vmXJmz64PXAoZfHYVoOAFh2nyQs= | ||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= | ||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/tls" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/BurntSushi/toml" | ||
"github.com/emersion/go-imap" | ||
"github.com/emersion/go-imap/client" | ||
"github.com/emersion/go-message/mail" | ||
) | ||
|
||
//config struct | ||
type KoboMailConfig struct { | ||
IMAP_Host string `toml:"imap_host"` | ||
IMAP_Port string `toml:"imap_port"` | ||
IMAP_User string `toml:"imap_user"` | ||
IMAP_Pwd string `toml:"imap_pwd"` | ||
Email_Flag string `toml:"email_flag"` | ||
Email_Unseen string `toml:"email_unseen"` | ||
} | ||
|
||
// chkErrFatal prints a message to the Kobo screen, then exits the program | ||
func chkErrFatal(err error, usrMsg string, msgDuration int) { | ||
if err != nil { | ||
if usrMsg != "" { | ||
// fbPrint(usrMsg) | ||
time.Sleep(time.Duration(msgDuration) * time.Second) | ||
} | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
// // logErrPrint is a convenience function for logging errors | ||
// func logErrPrint(err error) { | ||
// if err != nil { | ||
// log.Print(err) | ||
// } | ||
// } | ||
|
||
// nickelUSBplug simulates pugging in a USB cable | ||
func nickelUSBplug() { | ||
nickelHWstatusPipe := "/tmp/nickel-hardware-status" | ||
nickelPipe, _ := os.OpenFile(nickelHWstatusPipe, os.O_RDWR, os.ModeNamedPipe) | ||
nickelPipe.WriteString("usb plug add") | ||
nickelPipe.Close() | ||
} | ||
|
||
// nickelUSBunplug simulates unplugging a USB cable | ||
func nickelUSBunplug() { | ||
nickelHWstatusPipe := "/tmp/nickel-hardware-status" | ||
nickelPipe, _ := os.OpenFile(nickelHWstatusPipe, os.O_RDWR, os.ModeNamedPipe) | ||
nickelPipe.WriteString("usb plug remove") | ||
nickelPipe.Close() | ||
} | ||
|
||
func main() { | ||
// If the file doesn't exist, create it or append to the | ||
KM_Log_Path := "" | ||
if _, err := os.Stat("/mnt/onboard/.add/kobomail/logs.txt"); err == nil { | ||
KM_Log_Path = "/mnt/onboard/.add/kobomail/logs.txt" | ||
} else if os.IsNotExist(err) { | ||
KM_Log_Path = "logs.txt" | ||
} | ||
logFile, err := os.OpenFile(KM_Log_Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
//let's output to both stoud and log file | ||
mw := io.MultiWriter(os.Stdout, logFile) | ||
log.SetOutput(mw) | ||
|
||
KM_Library_Path := "" | ||
if _, err := os.Stat("/mnt/onboard/KoboMailLibrary/"); err == nil { | ||
KM_Library_Path = "/mnt/onboard/KoboMailLibrary/" | ||
} else if os.IsNotExist(err) { | ||
KM_Library_Path = "Library/" | ||
} | ||
|
||
// Read Config file | ||
KM_Config_Path := "" | ||
if _, err := os.Stat("/mnt/onboard/.add/kobomail/kobomail_cfg.toml"); err == nil { | ||
KM_Config_Path = "/mnt/onboard/.add/kobomail/kobomail_cfg.toml" | ||
} else if os.IsNotExist(err) { | ||
KM_Config_Path = "kobomail_cfg.toml" | ||
} | ||
|
||
var KM_Config KoboMailConfig | ||
if _, err := toml.DecodeFile(KM_Config_Path, &KM_Config); err != nil { | ||
chkErrFatal(err, "Couldn't read config. Aborting!", 5) | ||
} | ||
|
||
host := KM_Config.IMAP_Host | ||
port := KM_Config.IMAP_Port | ||
user := KM_Config.IMAP_User | ||
pass := KM_Config.IMAP_Pwd | ||
tlsn := "" | ||
if port == "" { | ||
port = "993" | ||
} | ||
|
||
connStr := fmt.Sprintf("%s:%s", host, port) | ||
|
||
tlsc := &tls.Config{} | ||
if tlsn != "" { | ||
tlsc.ServerName = tlsn | ||
} | ||
|
||
c, err := client.DialTLS(connStr, tlsc) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Println("Connected") | ||
defer c.Logout() | ||
|
||
if err := c.Login(user, pass); err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Println("Authenticated") | ||
|
||
mbox, err := c.Select("INBOX", false) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Println("Inbox selected: ", mbox.Name) | ||
|
||
criteria := imap.NewSearchCriteria() | ||
if KM_Config.Email_Unseen == "true" { | ||
criteria.WithoutFlags = []string{"\\Seen"} | ||
} | ||
KM_Config_To_Kobo := strings.Replace(KM_Config.IMAP_User, "@", "+"+KM_Config.Email_Flag+"@", 1) | ||
criteria.Header.Add("TO", KM_Config_To_Kobo) | ||
|
||
uids, err := c.Search(criteria) | ||
if err != nil { | ||
log.Println(err) | ||
} | ||
seqset := new(imap.SeqSet) | ||
seqset.AddNum(uids...) | ||
log.Printf("Search complete, found %d messages", len(uids)) | ||
|
||
section := &imap.BodySectionName{} | ||
items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchFlags, imap.FetchInternalDate, section.FetchItem()} | ||
messages := make(chan *imap.Message) | ||
done := make(chan error, 1) | ||
go func() { | ||
done <- c.Fetch(seqset, items, messages) | ||
log.Println("Fetch complete") | ||
}() | ||
|
||
for msg := range messages { | ||
if msg != nil { | ||
log.Printf("got message with address %p\n", msg) | ||
|
||
r := msg.GetBody(section) | ||
if r == nil { | ||
log.Fatal("Server didn't returned message body") | ||
} | ||
// Create a new mail reader | ||
mr, err := mail.CreateReader(r) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Print some info about the message | ||
header := mr.Header | ||
if date, err := header.Date(); err == nil { | ||
log.Println("Date:", date) | ||
} | ||
// if from, err := header.AddressList("From"); err == nil { | ||
// log.Println("From:", from) | ||
// } | ||
// if to, err := header.AddressList("To"); err == nil { | ||
// log.Println("To:", to) | ||
// } | ||
if subject, err := header.Subject(); err == nil { | ||
log.Println("Subject:", subject) | ||
} | ||
|
||
// Process each message's part | ||
for { | ||
p, err := mr.NextPart() | ||
if err == io.EOF { | ||
break | ||
} else if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
switch h := p.Header.(type) { | ||
case *mail.AttachmentHeader: | ||
// This is an attachment | ||
filename, _ := h.Filename() | ||
log.Println("Got attachment:", filename) | ||
contenttype, _, _ := h.ContentType() | ||
log.Println("of type: ", contenttype) | ||
b, _ := ioutil.ReadAll(p.Body) | ||
// write the whole body at once | ||
err = ioutil.WriteFile(KM_Library_Path+filename, b, 0644) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
} | ||
} | ||
} else { | ||
log.Println("no messages matched criteria") | ||
} | ||
} | ||
// if err := <-done; err != nil { | ||
// log.Fatal(err) | ||
// } | ||
|
||
//now that we finished loading all messages we'll simulate the USB cable connect | ||
//but only if there were any messages processed, no need to bug the user if there was nothing new | ||
if len(uids) > 0 { | ||
log.Println("Simulating PLugging USB cable and wait 10s for the user to click on the connect button") | ||
nickelUSBplug() | ||
|
||
time.Sleep(10 * time.Second) | ||
|
||
log.Println("Simulating unplugging USB cable") | ||
nickelUSBunplug() | ||
//after this Nickel will do the job to import the new files loaded into the KoboMailLibrary folder | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/bin/sh | ||
|
||
#GOOS=linux GOARCH=arm go build -o kobomail | ||
cp kobomail usr/local/kobomail/ | ||
tar -cvzf KoboRoot.tgz -C . etc usr |
Binary file not shown.
Oops, something went wrong.