1package xmpp 2 3import ( 4 "encoding/xml" 5 "errors" 6 "fmt" 7 8 "github.com/coyim/coyim/xmpp/data" 9 "github.com/coyim/coyim/xmpp/interfaces" 10 "github.com/coyim/coyim/xmpp/jid" 11) 12 13const ( 14 mucSupport = "<x xmlns='http://jabber.org/protocol/muc'/>" 15 mucNS = "http://jabber.org/protocol/muc" 16) 17 18func (c *conn) GetChatContext() interfaces.Chat { 19 return &muc{ 20 conn: c, 21 events: make(chan interface{}), 22 } 23} 24 25type muc struct { 26 *conn 27 events chan interface{} 28} 29 30func (m *muc) Events() chan interface{} { 31 return m.events 32} 33 34//See: Section "6.2 Discovering the Features Supported by a MUC Service" 35func (m *muc) CheckForSupport(entity string) bool { 36 return m.HasSupportTo(entity, mucNS) 37} 38 39//See: Section "6.3 Discovering Rooms" 40func (m *muc) QueryRooms(entity string) ([]data.DiscoveryItem, error) { 41 query, err := m.QueryServiceItems(entity) 42 if err != nil { 43 return nil, err 44 } 45 46 return query.DiscoveryItems, nil 47} 48 49//See: Section "6.4 Querying for Room Information" 50func (m *muc) QueryRoomInformation(room string) (*data.RoomInfo, error) { 51 j := jid.Parse(room) 52 if j == jid.Domain("") { 53 return nil, errors.New("invalid room") 54 } 55 56 local := string(jid.MaybeLocal(j)) 57 58 //TODO: this error is useless when it says ("expected query, got error") 59 //It should give us a xmpp error 60 query, err := m.queryRoomInformation(&data.Room{ 61 ID: local, 62 Service: string(j.Host()), 63 }) 64 65 if err != nil { 66 return nil, err 67 } 68 69 return parseRoomInformation(query), nil 70} 71 72func parseRoomInfoForm(forms []data.Form) data.RoomInfoForm { 73 ret := data.RoomInfoForm{} 74 parseForms(&ret, forms) 75 return ret 76} 77 78func parseRoomType(features []data.DiscoveryFeature) data.RoomType { 79 ret := data.RoomType{} 80 81 for _, f := range features { 82 switch f.Var { 83 case "muc_public": 84 ret.Public = true 85 case "muc_open": 86 ret.Open = true 87 case "muc_moderated": 88 ret.Moderated = true 89 case "muc_semianonymous": 90 ret.SemiAnonymous = true 91 case "muc_passwordprotected": 92 ret.PasswordProtected = true 93 case "muc_persistenc": 94 ret.Persistent = true 95 } 96 } 97 98 return ret 99} 100 101func parseRoomInformation(query *data.DiscoveryInfoQuery) *data.RoomInfo { 102 return &data.RoomInfo{ 103 RoomInfoForm: parseRoomInfoForm(query.Forms[:]), 104 RoomType: parseRoomType(query.Features), 105 } 106} 107 108func (m *muc) queryRoomInformation(room *data.Room) (*data.DiscoveryInfoQuery, error) { 109 return m.QueryServiceInformation(room.JID()) 110} 111 112//See: Section "7.2.2 Basic MUC Protocol" 113func (m *muc) EnterRoom(occupant *data.Occupant) error { 114 //TODO: Implement section "7.2.1 Groupchat 1.0 Protocol"? 115 return m.sendPresence(&data.ClientPresence{ 116 To: occupant.JID(), 117 Extra: mucSupport, 118 }) 119} 120 121//See: Section "7.14 Exiting a Room" 122func (m *muc) LeaveRoom(occupant *data.Occupant) error { 123 return m.sendPresence(&data.ClientPresence{ 124 To: occupant.JID(), 125 Type: "unavailable", 126 Extra: mucSupport, 127 }) 128} 129 130//See: Section "7.4 Sending a Message to All Occupants" 131func (m *muc) SendChatMessage(msg string, to *data.Room) error { 132 //TODO: How to disable archive for chat messages? 133 //TODO: Can we just use the same conn.Send() with a different type? 134 _, err := fmt.Fprintf(m.out, "<message "+ 135 "to='%s' "+ 136 "from='%s' "+ 137 "type='groupchat'>"+ 138 "<body>%s</body>"+ 139 "</message>", 140 xmlEscape(to.JID()), xmlEscape(m.conn.jid), xmlEscape(msg)) 141 return err 142} 143 144//See: Section "10.2 Subsequent Room Configuration" 145func (m *muc) RequestRoomConfigForm(room *data.Room) (*data.Form, error) { 146 reply, _, err := m.SendIQ(room.JID(), "get", &data.RoomConfigurationQuery{}) 147 148 stanza, ok := <-reply 149 if !ok { 150 return nil, errors.New("xmpp: failed to receive response") 151 } 152 153 iq, ok := stanza.Value.(*data.ClientIQ) 154 if !ok { 155 return nil, errors.New("xmpp: failed to parse response") 156 } 157 158 r := &data.RoomConfigurationQuery{} 159 err = xml.Unmarshal(iq.Query, r) 160 return r.Form, err 161} 162 163func (m *muc) RoomConfigForm(room *data.Room, formCallback data.FormCallback) error { 164 form, err := m.RequestRoomConfigForm(room) 165 if err != nil { 166 return err 167 } 168 169 var datas []data.BobData 170 roomConfig, err := processForm(form, datas, formCallback) 171 if err != nil { 172 return err 173 } 174 175 return m.UpdateRoomConfig(room, roomConfig) 176} 177 178//See: Section "10.2 Subsequent Room Configuration" 179func (m *muc) UpdateRoomConfig(room *data.Room, form *data.Form) error { 180 _, _, err := m.SendIQ(room.JID(), "set", &data.RoomConfigurationQuery{ 181 Form: form, 182 }) 183 184 return err 185} 186