diff options
| -rw-r--r-- | client/client.go | 12 | ||||
| -rw-r--r-- | cmd/client/main.go | 12 | ||||
| -rw-r--r-- | core/data.go | 6 | ||||
| -rw-r--r-- | core/payload.go | 49 | ||||
| -rw-r--r-- | docs/rfc.html | 68 | ||||
| -rw-r--r-- | docs/rfc.md | 26 | ||||
| -rw-r--r-- | docs/rfc.txt | 37 | ||||
| -rw-r--r-- | server/storage.go | 6 | ||||
| -rw-r--r-- | server/user.go | 22 | ||||
| -rw-r--r-- | storage/storage.go | 25 |
10 files changed, 256 insertions, 7 deletions
diff --git a/client/client.go b/client/client.go index ba53ec9..a9dfb2d 100644 --- a/client/client.go +++ b/client/client.go @@ -12,6 +12,7 @@ import ( type Handler interface { HandleMessage(msg core.Message) + HandleListItem(list core.ListItem) HandleError(err error) } @@ -110,6 +111,15 @@ func (c *Client) GetHistory(channel string, since time.Time, count, offset int) return core.Send(c.conn, hist) } +func (c *Client) GetList(count, offset int) error { + list := core.List{ + Count: int64(count), + Offset: int64(offset), + } + + return core.Send(c.conn, list) +} + func (c *Client) read() { for { payload, err := core.Read(c.conn) @@ -128,5 +138,7 @@ func (c *Client) handlePayload(payload any) { switch v := payload.(type) { case core.Message: c.h.HandleMessage(v) + case core.ListItem: + c.h.HandleListItem(v) } } diff --git a/cmd/client/main.go b/cmd/client/main.go index f135a69..e15d917 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -24,6 +24,10 @@ func (h *Handler) HandleMessage(msg core.Message) { fmt.Printf("%s\t%s -> %s %s\n", msg.Timestamp.Format("2006/01/02 15:04:05.000"), msg.Source, msg.Target, msg.Content) } +func (h *Handler) HandleListItem(li core.ListItem) { + fmt.Println(li.Address) +} + func (h *Handler) HandleError(err error) { log.Println("client error:", err) } @@ -33,6 +37,7 @@ func main() { prompt.Commands["join"] = join prompt.Commands["leave"] = leave prompt.Commands["history"] = history + prompt.Commands["list"] = list var cfg client.Config @@ -81,3 +86,10 @@ func history(args []string) { log.Println("cannot read channel history:", err) } } + +func list(args []string) { + err := c.GetList(99999999, 0) + if err != nil { + log.Println("cannot channels list:", err) + } +} diff --git a/core/data.go b/core/data.go index 6871a89..4705c75 100644 --- a/core/data.go +++ b/core/data.go @@ -25,6 +25,8 @@ const ( PayloadServerAuth = 0x06 PayloadUsermode = 0x07 PayloadHistory = 0x08 + PayloadList = 0x09 + PayloadListItem = 0x10 PayloadTest = 0xFF ) @@ -146,6 +148,10 @@ func Decode(buf io.Reader) (any, error) { return DecodeUsermode(buf) case PayloadHistory: return DecodeHistory(buf) + case PayloadList: + return DecodeList(buf) + case PayloadListItem: + return DecodeListItem(buf) case PayloadTest: return DecodeTest(buf) default: diff --git a/core/payload.go b/core/payload.go index be9d844..aad21ee 100644 --- a/core/payload.go +++ b/core/payload.go @@ -226,6 +226,55 @@ func DecodeHistory(buf io.Reader) (History, error) { return h, nil } +type List struct { + Count int64 + Offset int64 +} + +func (l List) Wrap() (PayloadType, []any) { + return PayloadList, []any{ + l.Count, + l.Offset, + } +} + +func DecodeList(buf io.Reader) (List, error) { + var l List + + err := decodeNumeric(buf, &l.Count) + if err != nil { + return l, err + } + + err = decodeNumeric(buf, &l.Offset) + if err != nil { + return l, err + } + + return l, nil +} + +type ListItem struct { + Address string +} + +func (l ListItem) Wrap() (PayloadType, []any) { + return PayloadListItem, []any{ + l.Address, + } +} + +func DecodeListItem(buf io.Reader) (ListItem, error) { + var l ListItem + + err := decodeString(buf, &l.Address) + if err != nil { + return l, err + } + + return l, nil +} + type Test struct { Num1 uint8 Time1 time.Time diff --git a/docs/rfc.html b/docs/rfc.html index 373526e..297f593 100644 --- a/docs/rfc.html +++ b/docs/rfc.html @@ -1367,6 +1367,12 @@ SOLEC system.<a href="#section-abstract-1" class="pilcrow">¶</a></p> <li class="compact toc ulBare ulEmpty" id="section-toc.1-1.2.2.4.2.9"> <p id="section-toc.1-1.2.2.4.2.9.1"><a href="#section-2.4.9" class="auto internal xref">2.4.9</a>. <a href="#name-test" class="internal xref">Test</a></p> </li> + <li class="compact toc ulBare ulEmpty" id="section-toc.1-1.2.2.4.2.10"> + <p id="section-toc.1-1.2.2.4.2.10.1"><a href="#section-2.4.10" class="auto internal xref">2.4.10</a>. <a href="#name-list" class="internal xref">List</a></p> +</li> + <li class="compact toc ulBare ulEmpty" id="section-toc.1-1.2.2.4.2.11"> + <p id="section-toc.1-1.2.2.4.2.11.1"><a href="#section-2.4.11" class="auto internal xref">2.4.11</a>. <a href="#name-listitem" class="internal xref">ListItem</a></p> +</li> </ul> </li> <li class="compact toc ulBare ulEmpty" id="section-toc.1-1.2.2.5"> @@ -1623,10 +1629,20 @@ Text is encoded using UTF-8.<a href="#section-2.3.3-1" class="pilcrow">¶</a></p </tr> <tr> <td class="text-left" rowspan="1" colspan="1">0x08</td> - <td class="text-left" rowspan="1" colspan="1">History.</td> + <td class="text-left" rowspan="1" colspan="1">History</td> <td class="text-left" rowspan="1" colspan="1">C</td> </tr> <tr> + <td class="text-left" rowspan="1" colspan="1">0x09</td> + <td class="text-left" rowspan="1" colspan="1">List</td> + <td class="text-left" rowspan="1" colspan="1">CE</td> + </tr> + <tr> + <td class="text-left" rowspan="1" colspan="1">0x10</td> + <td class="text-left" rowspan="1" colspan="1">ListItem</td> + <td class="text-left" rowspan="1" colspan="1">S</td> + </tr> + <tr> <td class="text-left" rowspan="1" colspan="1">0xFF</td> <td class="text-left" rowspan="1" colspan="1">Test</td> <td class="text-left" rowspan="1" colspan="1">R</td> @@ -1981,6 +1997,56 @@ should ignore this kind of payload.<a href="#section-2.4.9-1" class="pilcrow">¶ </table> </section> </div> +<div id="list"> +<section id="section-2.4.10"> + <h4 id="name-list"> +<a href="#section-2.4.10" class="section-number selfRef">2.4.10. </a><a href="#name-list" class="section-name selfRef">List</a> + </h4> +<p id="section-2.4.10-1">Request list of channels and users that client can send messages to. Number of retrieved items can be limited using <em>count</em> and <em>offset</em> fields.<a href="#section-2.4.10-1" class="pilcrow">¶</a></p> +<table class="center" id="table-13"> + <caption><a href="#table-13" class="selfRef">Table 13</a></caption> +<thead> + <tr> + <th class="text-left" rowspan="1" colspan="1">Type</th> + <th class="text-left" rowspan="1" colspan="1">Name</th> + </tr> + </thead> + <tbody> + <tr> + <td class="text-left" rowspan="1" colspan="1">int64</td> + <td class="text-left" rowspan="1" colspan="1">count</td> + </tr> + <tr> + <td class="text-left" rowspan="1" colspan="1">int64</td> + <td class="text-left" rowspan="1" colspan="1">offset</td> + </tr> + </tbody> + </table> +</section> +</div> +<div id="listitem"> +<section id="section-2.4.11"> + <h4 id="name-listitem"> +<a href="#section-2.4.11" class="section-number selfRef">2.4.11. </a><a href="#name-listitem" class="section-name selfRef">ListItem</a> + </h4> +<p id="section-2.4.11-1"><em>ListItem</em> is send as a reply to <em>List</em> request. Multiple list items are sent in separate payloads.<a href="#section-2.4.11-1" class="pilcrow">¶</a></p> +<table class="center" id="table-14"> + <caption><a href="#table-14" class="selfRef">Table 14</a></caption> +<thead> + <tr> + <th class="text-left" rowspan="1" colspan="1">Type</th> + <th class="text-left" rowspan="1" colspan="1">Name</th> + </tr> + </thead> + <tbody> + <tr> + <td class="text-left" rowspan="1" colspan="1">string</td> + <td class="text-left" rowspan="1" colspan="1">address</td> + </tr> + </tbody> + </table> +</section> +</div> </section> </div> <div id="sequential-operations"> diff --git a/docs/rfc.md b/docs/rfc.md index d6c8085..ecfbdc0 100644 --- a/docs/rfc.md +++ b/docs/rfc.md @@ -161,9 +161,11 @@ Payload type attributes describes following characteristics: | 0x03 | Handshake | SC | | 0x04 | UserAuth | C | | 0x05 | Message | SC | -| 0x06 | ServerAuth | S | -| 0x07 | UserMode | C | -| 0x08 | History. | C | +| 0x06 | ServerAuth | S | +| 0x07 | UserMode | C | +| 0x08 | History | C | +| 0x09 | List | CE | +| 0x10 | ListItem | S | | 0xFF | Test | R | ### Success @@ -273,6 +275,24 @@ should ignore this kind of payload. | string | str3 | | uint64 | num4 | +### List + +Request list of channels and users that client can send messages to. Number of retrieved items can be limited using *count* and *offset* fields. + +| Type | Name | +|-----------|-----------------| +| int64 | count | +| int64 | offset | + +### ListItem + +*ListItem* is send as a reply to *List* request. Multiple list items are sent in separate payloads. + +| Type | Name | +|--------|---------| +| string | address | + + ## Sequential operations Some operations require multiple rounds of communication. diff --git a/docs/rfc.txt b/docs/rfc.txt index 335ea3d..173c1bd 100644 --- a/docs/rfc.txt +++ b/docs/rfc.txt @@ -35,6 +35,8 @@ Table of Contents 2.4.7. Usermode 2.4.8. History 2.4.9. Test + 2.4.10. List + 2.4.11. ListItem 2.5. Sequential operations 2.6. Client-Server connection initialisation 2.7. Exchanging messages between servers @@ -180,7 +182,11 @@ Table of Contents +------+------------+------------+ | 0x07 | UserMode | C | +------+------------+------------+ - | 0x08 | History. | C | + | 0x08 | History | C | + +------+------------+------------+ + | 0x09 | List | CE | + +------+------------+------------+ + | 0x10 | ListItem | S | +------+------------+------------+ | 0xFF | Test | R | +------+------------+------------+ @@ -368,6 +374,35 @@ Table of Contents Table 12 +2.4.10. List + + Request list of channels and users that client can send messages to. + Number of retrieved items can be limited using _count_ and _offset_ + fields. + + +=======+========+ + | Type | Name | + +=======+========+ + | int64 | count | + +-------+--------+ + | int64 | offset | + +-------+--------+ + + Table 13 + +2.4.11. ListItem + + _ListItem_ is send as a reply to _List_ request. Multiple list items + are sent in separate payloads. + + +========+=========+ + | Type | Name | + +========+=========+ + | string | address | + +--------+---------+ + + Table 14 + 2.5. Sequential operations Some operations require multiple rounds of communication. In this diff --git a/server/storage.go b/server/storage.go index 99402c8..bffb783 100644 --- a/server/storage.go +++ b/server/storage.go @@ -8,8 +8,8 @@ import ( type Storage interface { AddMessage(msg core.Message) (err error) - GetHistory(target string, since time.Time, num, offset int) (history []core.Message, err error) - GetHistoryUser(user1, user2 string, since time.Time, num, offset int) (history []core.Message, err error) + GetHistory(target string, since time.Time, count, offset int) (history []core.Message, err error) + GetHistoryUser(user1, user2 string, since time.Time, count, offset int) (history []core.Message, err error) SetUser(user core.UserData) error DelUser(name string) error @@ -17,5 +17,7 @@ type Storage interface { SetPermission(data core.PermissionData) error GetPermission(user, channel string) (core.PermissionData, error) + GetChannelUsers(channel string) ([]string, error) + GetUserChannels(user string, count, offset int) ([]string, error) } diff --git a/server/user.go b/server/user.go index 69b7ced..5e29c8d 100644 --- a/server/user.go +++ b/server/user.go @@ -126,6 +126,8 @@ func (s *Server) handleUserPayload(user *User, sender net.Conn, payload any) err return s.handleUsermode(user, sender, v) case core.History: return s.handleHistory(user, sender, v) + case core.List: + return s.handleList(user, sender, v) default: return core.ErrUnexpectedPayloadType } @@ -206,3 +208,23 @@ func (s *Server) handleHistory(user *User, conn net.Conn, hist core.History) err return nil } + +func (s *Server) handleList(user *User, conn net.Conn, list core.List) error { + channels, err := s.Storage.GetUserChannels(user.Addr, int(list.Count), int(list.Offset)) + if err != nil { + return fmt.Errorf("cannot get user channels list: %v", err) + } + + for _, c := range channels { + li := core.ListItem{c} + data, err := core.Encode(li) + if err != nil { + return err + } + if _, err := conn.Write(data); err != nil { + return err + } + } + + return nil +} diff --git a/storage/storage.go b/storage/storage.go index a32c4a0..6455876 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -169,6 +169,31 @@ func (db *Database) GetChannelUsers(channel string) (users []string, err error) return users, nil } +func (db *Database) GetUserChannels(user string, count, offset int) (channels []string, err error) { + rows, err := db.Query("SELECT channel FROM permissions WHERE user = ? AND write = 1;", user) + defer func() { + if rows == nil { + return + } + if err := rows.Close(); err != nil { + log.Println("cannot close database row:", err) + } + }() + if err != nil { + return channels, err + } + + for rows.Next() { + var channel string + if err := rows.Scan(&channel); err != nil { + return channels, err + } + channels = append(channels, channel) + } + + return channels, nil +} + func itob(v int) bool { if v == 1 { return true |
