aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client.go40
-rw-r--r--cmd/kbbi/main.go74
-rw-r--r--direct_client.go120
-rw-r--r--direct_client_test.go2
-rw-r--r--server.go16
5 files changed, 137 insertions, 115 deletions
diff --git a/client.go b/client.go
index c23100f..bf8c599 100644
--- a/client.go
+++ b/client.go
@@ -5,7 +5,7 @@
package kbbi
import (
- "net/http"
+ "fmt"
)
//
@@ -19,19 +19,18 @@ type Client struct {
//
// NewClient create and initialize new client.
-// If cookies is not empty, the direct client will be initialized and actived.
//
-func NewClient(cookies []*http.Cookie) (cl *Client, err error) {
+func NewClient() (cl *Client, err error) {
cl = &Client{}
- cl.direct, err = newDirectClient(cookies)
+ cl.direct, err = newDirectClient()
if err != nil {
return nil, err
}
cl.api = newAPIClient("")
- if cookies != nil {
+ if cl.IsAuthenticated() {
cl.active = cl.direct
} else {
cl.active = cl.api
@@ -59,6 +58,14 @@ func (cl *Client) CariDefinisi(words []string) (
}
//
+// IsAuthenticated will return true if client has logged in to KBBI official
+// server.
+//
+func (cl *Client) IsAuthenticated() bool {
+ return cl.direct.isAuthenticated()
+}
+
+//
// ListKataDasar list all of the root words in dictionary.
//
func (cl *Client) ListKataDasar() (res DaftarKata, err error) {
@@ -78,26 +85,13 @@ func (cl *Client) ListKataDasar() (res DaftarKata, err error) {
// Login authenticate the client using username and password to official KBBI
// server.
//
-func (cl *Client) Login(user, pass string) (cookies []*http.Cookie, err error) {
- if cl.direct == nil {
- cl.direct, err = newDirectClient(nil)
- if err != nil {
- return nil, err
- }
- }
- cookies, err = cl.direct.login(user, pass)
+func (cl *Client) Login(user, pass string) (err error) {
+ err = cl.direct.login(user, pass)
if err != nil {
- return nil, err
+ return fmt.Errorf("Login: %w", err)
}
+
cl.active = cl.direct
- return cookies, nil
-}
-//
-// SetCookies for HTTP request in direct client.
-//
-func (cl *Client) SetCookies(cookies []*http.Cookie) {
- if cl.direct != nil {
- cl.direct.setCookies(cookies)
- }
+ return nil
}
diff --git a/cmd/kbbi/main.go b/cmd/kbbi/main.go
index adba621..3b07c36 100644
--- a/cmd/kbbi/main.go
+++ b/cmd/kbbi/main.go
@@ -2,19 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//
+// Program kbbi adalah antar-muka perintah untuk Kamus Besar Bahasa Indonesia
+// (KBBI) menggunakan API.
+//
package main
import (
- "bytes"
- "encoding/gob"
- "errors"
"flag"
"fmt"
- "io/ioutil"
"log"
- "net/http"
- "os"
- "path/filepath"
"sort"
"github.com/shuLhan/kbbi"
@@ -34,14 +31,6 @@ func main() {
log.SetFlags(0)
- baseDir, err := os.UserConfigDir()
- if err != nil {
- log.Fatal("kbbi.createConfigDir:", err)
- }
-
- createConfigDir(baseDir)
- cookies := loadCookies(baseDir)
-
flag.StringVar(&surel, "surel", "", "Nama pengguna")
flag.StringVar(&sandi, "sandi", "", "Sandi pengguna")
flag.BoolVar(&isListKataDasar, "daftar-kata-dasar", false,
@@ -49,25 +38,22 @@ func main() {
flag.Parse()
- cl, err := kbbi.NewClient(cookies)
+ cl, err := kbbi.NewClient()
if err != nil {
log.Fatal(err)
}
if len(surel) > 0 && len(sandi) > 0 {
- cookies, err = cl.Login(surel, sandi)
+ err = cl.Login(surel, sandi)
if err != nil {
log.Fatal("kbbi: ", err)
}
- saveCookies(baseDir, cookies)
- cl.SetCookies(cookies)
}
if isListKataDasar {
- if len(cookies) == 0 {
+ if cl.IsAuthenticated() {
log.Fatal("opsi -daftar-kata-dasar membutuhkan opsi -surel dan -sandi")
}
-
listKataDasar(cl)
return
}
@@ -106,13 +92,6 @@ func main() {
}
}
-func createConfigDir(baseDir string) {
- err := os.MkdirAll(filepath.Join(baseDir, configDir), 0700)
- if err != nil {
- log.Fatal("kbbi.createConfigDir:", err)
- }
-}
-
func listKataDasar(cl *kbbi.Client) {
kataDasar, err := cl.ListKataDasar()
if err != nil {
@@ -131,42 +110,3 @@ func listKataDasar(cl *kbbi.Client) {
fmt.Println(kata)
}
}
-
-func loadCookies(baseDir string) (cookies []*http.Cookie) {
- f := filepath.Join(baseDir, configDir, cookieFile)
-
- _, err := os.Stat(f)
- if errors.Is(err, os.ErrNotExist) {
- return nil
- }
-
- body, err := ioutil.ReadFile(f)
- if err != nil {
- log.Fatal("kbbi.loadCookies: ", err)
- }
-
- dec := gob.NewDecoder(bytes.NewReader(body))
-
- err = dec.Decode(&cookies)
- if err != nil {
- log.Println("kbbi.loadCookies: ", err)
- }
-
- return cookies
-}
-
-func saveCookies(baseDir string, cookies []*http.Cookie) {
- f := filepath.Join(baseDir, configDir, cookieFile)
-
- var buf bytes.Buffer
- enc := gob.NewEncoder(&buf)
- err := enc.Encode(cookies)
- if err != nil {
- log.Fatal("kbbi.saveCookies: ", err)
- }
-
- err = ioutil.WriteFile(f, buf.Bytes(), 0600)
- if err != nil {
- log.Fatal("kbbi.saveCookies: ", err)
- }
-}
diff --git a/direct_client.go b/direct_client.go
index 75f1404..aa296b5 100644
--- a/direct_client.go
+++ b/direct_client.go
@@ -6,12 +6,16 @@ package kbbi
import (
"bytes"
+ "encoding/gob"
+ "errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
+ "os"
+ "path/filepath"
"strconv"
"strings"
@@ -21,6 +25,8 @@ import (
)
const (
+ cookieFile = "cookie"
+ configDir = "kbbi"
maxPageNumber = 501
)
@@ -28,7 +34,9 @@ const (
// directClient for KBBI web using HTTP.
//
type directClient struct {
+ baseDir string
cookieURL *url.URL
+ cookies []*http.Cookie
httpc *http.Client
}
@@ -36,7 +44,7 @@ type directClient struct {
// newDirectClient create and initialize new client that connect directly to
// KBBI official website.
//
-func newDirectClient(cookies []*http.Cookie) (cl *directClient, err error) {
+func newDirectClient() (cl *directClient, err error) {
cookieURL, err := url.Parse(baseURL)
if err != nil {
return nil, fmt.Errorf("newDirectClient: %w", err)
@@ -48,11 +56,7 @@ func newDirectClient(cookies []*http.Cookie) (cl *directClient, err error) {
jar, err := cookiejar.New(jarOpt)
if err != nil {
- return nil, err
- }
-
- if cookies != nil {
- jar.SetCookies(cookieURL, cookies)
+ return nil, fmt.Errorf("newDirectClient: %w", err)
}
cl = &directClient{
@@ -63,6 +67,15 @@ func newDirectClient(cookies []*http.Cookie) (cl *directClient, err error) {
},
}
+ err = cl.loadCookies()
+ if err != nil {
+ return nil, fmt.Errorf("newDirectClient: %w", err)
+ }
+
+ if cl.cookies != nil {
+ jar.SetCookies(cookieURL, cl.cookies)
+ }
+
return cl, nil
}
@@ -169,20 +182,26 @@ func (cl *directClient) ListKataDasar() (kataDasar DaftarKata, err error) {
}
//
-// Login authenticate the client using username and password.
+// isAuthenticated will return true if the client already login; otherwise it
+// will return false.
//
-func (cl *directClient) login(user, pass string) (
- cookies []*http.Cookie, err error,
-) {
+func (cl *directClient) isAuthenticated() bool {
+ return len(cl.cookies) > 0
+}
+
+//
+// login authenticate the client using username and password.
+//
+func (cl *directClient) login(surel, sandi string) (err error) {
tokenLogin, err := cl.preLogin()
if err != nil {
- return nil, fmt.Errorf("Login: %w", err)
+ return fmt.Errorf("Login: %w", err)
}
params := url.Values{
paramNameRequestVerificationToken: []string{tokenLogin},
- paramNamePosel: []string{user},
- paramNameKataSandi: []string{pass},
+ paramNamePosel: []string{surel},
+ paramNameKataSandi: []string{sandi},
paramNameIngatSaya: []string{paramValueFalse},
}
@@ -190,39 +209,39 @@ func (cl *directClient) login(user, pass string) (
req, err := http.NewRequest(http.MethodPost, loginURL, reqBody)
if err != nil {
- return nil, fmt.Errorf("Login: %w", err)
+ return fmt.Errorf("Login: %w", err)
}
req.Header.Set(headerNameContentType, headerValueContentType)
res, err := cl.httpc.Do(req)
if err != nil {
- return nil, fmt.Errorf("Login: %w", err)
+ return fmt.Errorf("Login: %w", err)
}
defer res.Body.Close()
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
- return nil, fmt.Errorf("Login: %w", err)
+ return fmt.Errorf("Login: %w", err)
}
if res.StatusCode >= http.StatusBadRequest {
- return nil, fmt.Errorf("Login: %d %s", res.StatusCode, resBody)
+ return fmt.Errorf("login: %d %s", res.StatusCode, resBody)
}
- cookies = cl.httpc.Jar.Cookies(cl.cookieURL)
+ cl.cookies = cl.httpc.Jar.Cookies(cl.cookieURL)
+ cl.setCookies()
+ cl.saveCookies()
- return cookies, nil
+ return nil
}
//
// setCookies for HTTP request that need an authentication.
//
-func (cl *directClient) setCookies(cookies []*http.Cookie) {
- if len(cookies) > 0 {
- cl.httpc.Jar.SetCookies(cl.cookieURL, cookies)
- }
+func (cl *directClient) setCookies() {
+ cl.httpc.Jar.SetCookies(cl.cookieURL, cl.cookies)
}
func (cl *directClient) parseHTMLKataDasar(htmlBody []byte) (
@@ -309,3 +328,58 @@ func (cl *directClient) preLogin() (token string, err error) {
return token, nil
}
+
+//
+// loadCookies load the KBBI cookies from file.
+//
+func (cl *directClient) loadCookies() (err error) {
+ cl.baseDir, err = os.UserConfigDir()
+ if err != nil {
+ return fmt.Errorf("loadCookies: %w", err)
+ }
+
+ f := filepath.Join(cl.baseDir, configDir, cookieFile)
+
+ _, err = os.Stat(f)
+ if errors.Is(err, os.ErrNotExist) {
+ return nil
+ }
+
+ body, err := ioutil.ReadFile(f)
+ if err != nil {
+ return fmt.Errorf("loadCookies: %w", err)
+ }
+
+ dec := gob.NewDecoder(bytes.NewReader(body))
+
+ err = dec.Decode(&cl.cookies)
+ if err != nil {
+ return fmt.Errorf("loadCookies: %w", err)
+ }
+
+ return nil
+}
+
+//
+// saveCookies store the client cookies to the file for future use.
+//
+func (cl *directClient) saveCookies() {
+ err := os.MkdirAll(filepath.Join(cl.baseDir, configDir), 0700)
+ if err != nil {
+ log.Println("saveCookies:", err)
+ }
+
+ f := filepath.Join(cl.baseDir, configDir, cookieFile)
+
+ var buf bytes.Buffer
+ enc := gob.NewEncoder(&buf)
+ err = enc.Encode(cl.cookies)
+ if err != nil {
+ log.Println("saveCookies: ", err)
+ }
+
+ err = ioutil.WriteFile(f, buf.Bytes(), 0600)
+ if err != nil {
+ log.Println("saveCookies: ", err)
+ }
+}
diff --git a/direct_client_test.go b/direct_client_test.go
index 894b876..dd3b59b 100644
--- a/direct_client_test.go
+++ b/direct_client_test.go
@@ -15,7 +15,7 @@ func TestDirectClient_parseHTMLKataDasar(t *testing.T) {
t.Fatal(err)
}
- cl, err := newDirectClient(nil)
+ cl, err := newDirectClient()
if err != nil {
t.Fatal(err)
}
diff --git a/server.go b/server.go
index f4401e8..aea5982 100644
--- a/server.go
+++ b/server.go
@@ -8,6 +8,7 @@ import (
"fmt"
"log"
stdhttp "net/http"
+ "os"
"strings"
"sync"
"time"
@@ -17,6 +18,8 @@ import (
)
const (
+ envKbbiSandi = "KBBI_SANDI"
+ envKbbiSurel = "KBBI_SUREL"
defRootDir = "_www-kbbi"
defListen = ":3394"
emptyResponse = "{}"
@@ -68,7 +71,7 @@ func NewServer(kamusStorage string) (server *Server, err error) {
return nil, fmt.Errorf("http.NewServer: %w", err)
}
- server.forwardc, err = newDirectClient(nil)
+ server.forwardc, err = newDirectClient()
if err != nil {
return nil, fmt.Errorf("http.NewServer: %w", err)
}
@@ -78,6 +81,17 @@ func NewServer(kamusStorage string) (server *Server, err error) {
return nil, fmt.Errorf("http.NewServer: %w", err)
}
+ if !server.forwardc.isAuthenticated() {
+ surel := os.Getenv(envKbbiSurel)
+ sandi := os.Getenv(envKbbiSandi)
+ if len(surel) > 0 && len(sandi) > 0 {
+ err = server.forwardc.login(surel, sandi)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
return server, nil
}